全部產品
Search
文件中心

ApsaraVideo VOD:通過ApsaraVideo for VODAPI上傳媒體檔案

更新時間:Jul 19, 2024

如需使用ApsaraVideo for VOD為音視頻等媒體檔案進行ApsaraVideo for Media Processing(轉碼、審核、添加浮水印等)、分發播放等操作,則需要先將媒體檔案上傳到ApsaraVideo for VOD中。本文以Java SDK調用API為例介紹如何調用API上傳媒體檔案,並提供完整的API上傳Demo供參考。

背景資訊

通過ApsaraVideo for VODAPI上傳,實際是基於OSS原生SDK上傳,需要開發人員自行實現所有上傳邏輯,包括在點播服務擷取上傳地址和憑證、Base64解碼上傳憑證和地址、以及調用OSS能力完成上傳。此方式較為繁瑣且出錯率較高,因此,本文封裝完整API上傳Demo供開發人員參考,以避免您在通過ApsaraVideo for VODAPI上傳時,出現媒資一直處於上傳中的問題。

說明
  • 點播API上傳是基於OSS的上傳,更多上傳原理請參見OSS上傳概述

  • 本文以Java語言為例提供說明,其他語言可以參考代碼邏輯自行實現,或參考基於OSS原生SDK上傳

  • 更多諮詢及建議,請加入服務DingTalk群(群號:11370001915)擷取支援人員。

前提條件

步驟一:安裝點播服務端SDK

操作指引請參見點播Java SDK安裝

步驟二:安裝OSS SDK

操作指引請參見OSS Java SDK安裝

步驟三:上傳

以下程式碼範例展示了如何調用OSS能力來實現本地上傳、網路流/網路URL上傳、本地M3U8上傳、分區上傳以及斷點續傳,如何在點播服務擷取上傳地址和憑證、Base64解碼上傳憑證和地址等完整的上傳樣本請參見完整程式碼範例

本地上傳

public static void uploadLocalFile(OSSClient ossClient, JSONObject uploadAddress, String localFile) {
        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");

        try {
            File file = new File(localFile);
            PutObjectResult result = ossClient.putObject(bucketName, objectName, file);
            // 如果上傳成功,則返回200。
            // 上傳檔案的同時指定進度條參數。此處UploadOss為調用類的類名,請在實際使用時替換為相應的類名。
//            PutObjectResult result = ossClient.putObject(new PutObjectRequest(bucketName,objectName, file).
//                    <PutObjectRequest>withProgressListener(new UploadOss()));
//            System.out.println(result.getResponse().getStatusCode());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 關閉OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

網路流、網路URL上傳

public static void uploadURLFile(OSSClient ossClient, JSONObject uploadAddress, String url) {
    String bucketName = uploadAddress.getString("Bucket");
    String objectName = uploadAddress.getString("FileName");

    try {
        InputStream inputStream = new URL(url).openStream();
        PutObjectResult result = ossClient.putObject(bucketName, objectName, inputStream);
        // 如果上傳成功,則返回200。
        System.out.println(result.getResponse().getStatusCode());
    } catch (OSSException oe) {
        System.out.println("Caught an OSSException, which means your request made it to OSS, "
                + "but was rejected with an error response for some reason.");
        System.out.println("Error Message:" + oe.getErrorMessage());
        System.out.println("Error Code:" + oe.getErrorCode());
        System.out.println("Request ID:" + oe.getRequestId());
        System.out.println("Host ID:" + oe.getHostId());
    } catch (Throwable ce) {
        System.out.println("Caught an ClientException, which means the client encountered "
                + "a serious internal problem while trying to communicate with OSS, "
                + "such as not being able to access the network.");
        System.out.println("Error Message:" + ce.getMessage());
    } finally {
        // 關閉OSSClient。
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }
}

本地M3U8上傳

說明

上傳本地M3U8音視頻檔案(包括所有分區檔案)到點播,需上傳本地M3U8索引檔案地址和所有分區地址。

public static void uploadLocalM3U8(OSSClient ossClient, JSONObject uploadAddress, String indexFile, String[] tsFiles ) {

        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");
        String objectPrefix = uploadAddress.getString("ObjectPrefix");
        System.out.println(uploadAddress.toJSONString());

        try {
            //上傳索引檔案indexFile
            File index = new File(indexFile);
            ossClient.putObject(bucketName, objectName, index);
            Thread.sleep(200);
            //上傳ts檔案  tsFiles
            for(String filePath : tsFiles){
                File ts = new File(filePath);
                ossClient.putObject(bucketName, objectPrefix + ts.getName(), ts);
                Thread.sleep(200);
            }
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 關閉OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

分區上傳以及斷點續傳

public static void uploadEnableCheckPointFile(OSSClient ossClient, JSONObject uploadAddress, String localFile) {
    String bucketName = uploadAddress.getString("Bucket");
    String objectName = uploadAddress.getString("FileName");

    try {
        UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName,objectName);
        // 通過UploadFileRequest設定單個參數。
        // 填寫本地檔案的完整路徑,例如D:\\localpath\\examplefile.mp4。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
        uploadFileRequest.setUploadFile(localFile);
        // 指定上傳並發線程數,預設值為1。
        uploadFileRequest.setTaskNum(5);
        // 指定上傳的分區大小,單位為位元組,取值範圍為100 KB~5 GB。預設值為100 KB。
        uploadFileRequest.setPartSize(1 * 1024 * 1024);
        // 開啟斷點續傳,預設關閉。
        uploadFileRequest.setEnableCheckpoint(true);
        // 記錄本地分區上傳結果的檔案。上傳過程中的進度資訊會儲存在該檔案中,如果某一分區上傳失敗,再次上傳時會根據檔案中記錄的點繼續上傳。上傳完成後,該檔案會被刪除。
        // 如果未設定該值,預設與待上傳的本地檔案同路徑,名稱為${uploadFile}.ucp。
        //uploadFileRequest.setCheckpointFile("yourCheckpointFile");
        // 斷點續傳上傳。
        ossClient.uploadFile(uploadFileRequest);

    } catch (OSSException oe) {
        System.out.println("Caught an OSSException, which means your request made it to OSS, "
                + "but was rejected with an error response for some reason.");
        System.out.println("Error Message:" + oe.getErrorMessage());
        System.out.println("Error Code:" + oe.getErrorCode());
        System.out.println("Request ID:" + oe.getRequestId());
        System.out.println("Host ID:" + oe.getHostId());
    } catch (Throwable ce) {
        System.out.println("Caught an ClientException, which means the client encountered "
                + "a serious internal problem while trying to communicate with OSS, "
                + "such as not being able to access the network.");
        System.out.println("Error Message:" + ce.getMessage());
    } finally {
        // 關閉OSSClient。
        if (ossClient != null) {
            ossClient.shutdown();
        }
    }
}

完整程式碼範例

import com.alibaba.fastjson.JSONObject;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.event.ProgressListener;
import com.aliyun.oss.model.*;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vod.model.v20170321.*;
import org.apache.commons.codec.binary.Base64;

import java.io.File;
import java.io.InputStream;
import java.net.URL;

/**
 * *****   使用須知     ******!!
 * 以下Java範例程式碼示範了如何通過API 方式上傳媒資檔案至ApsaraVideo for VOD。OSS部分詳細請參考 https://www.alibabacloud.com/help/object-storage-service/latest/overview-3
 * 此demo不同於上傳SDK,如何使用上傳SDK參考 https://www.alibabacloud.com/help/apsaravideo-for-vod/latest/upload-sdk-overview
 * <p>
 * 一、普通音視頻上傳
 *
 * 1.上傳本地檔案,使用分區上傳,並支援斷點續傳。
 * 1.1 當斷點續傳關閉時,最大支援上傳任務執行時間為3000秒,具體可上傳檔案大小與您的網路頻寬及磁碟讀寫能力有關。 參見uploadEnableCheckPointFile函數。
 * 1.2 當斷點續傳開啟時,最大支援48.8TB的單個檔案,注意,斷點續傳開啟後,上傳任務執行過程中,同時會將當前上傳位置寫入本地磁碟檔案,影響您上傳檔案的速度,請您根據檔案大小選擇是否開啟
 *
 * 2.上傳網路流,可指定檔案URL進行上傳,最大支援48.8TB的單個檔案。參見uploadURLFile函數。
 *
 * 3.上傳檔案流,可指定本地檔案進行上傳,不支援斷點續傳,最大支援5GB的單個檔案。參見uploadLocalFile函數。
 *
 * <p>
 * 二、m3u8檔案上傳:
 * 1.上傳本地m3u8音視頻檔案(包括所有分區檔案)到點播,需上傳本地m3u8索引檔案地址和所有分區地址。參見uploadLocalM3U8函數
 * 2.上傳網路m3u8音視頻檔案建議使用URL方式。
 *
 * 註:
 * 1) 上傳網路m3u8音視頻檔案時需要保證地址可訪問,如果有許可權限制,請設定帶簽名資訊的地址,且保證足夠長的有效期間,防止地址無法訪問導致上傳失敗
 * <p>
 *
 * 三、上傳進度:
 * 1.預設上傳進度回呼函數:必須實現ProgressListener類,若不需要可不實現此類。參考 https://www.alibabacloud.com/help/object-storage-service/latest/upload-progress-bars-4;
 * 2.自訂上傳進度回呼函數:您可根據自已的業務情境重新定義不同事件處理的方式,只需要修改上傳回調樣本函數即可。
 * 3.本demo中,參見第 225 行程式碼範例
 *
 * <p>
 *
 * 四、介面說明:
 * 1.CreateUploadVideo 和 RefreshUploadVideo 都只是擷取憑證的介面,根據業務需要二選一即可
 * 2.RefreshUploadVideo 可用於憑證逾時後重新擷取,也可用於覆蓋檔案上傳,videoId保持不變
 * 3.CreateUploadImage  可用於擷取圖片上傳憑證
 * 4.CreateUploadAttachedMedia  可用於擷取輔助媒資上傳憑證
 * 3.介面詳細說明參考 https://www.alibabacloud.com/help/apsaravideo-for-vod/latest/api-doc-vod-2017-03-21-api-overview
 *
 * <p>
 *
 * 注意:
 * 請替換樣本中的必選參數,樣本中的選擇性參數如果您不需要設定,請將其刪除,以免設定無效參數值與您的預期不符。
 */
public class UploadVodByApiDemo implements ProgressListener {

    //上傳進度條相關參數,非必須
    private long bytesWritten = 0;
    private long totalBytes = -1;
    private boolean succeed = false;

    public static void main(String[] argv) {
        // 阿里雲帳號AccessKey擁有所有API的存取權限,建議您使用RAM使用者進行API訪問或日常營運。
        // 強烈建議不要把AccessKey ID和AccessKey Secret儲存到工程代碼裡,否則可能導致AccessKey泄露,威脅您帳號下所有資源的安全。
        // 本樣本通過從環境變數中讀取AccessKey,來實現API訪問的身分識別驗證。運行程式碼範例前,請配置環境變數ALIBABA_CLOUD_ACCESS_KEY_ID和ALIBABA_CLOUD_ACCESS_KEY_SECRET。
        String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
        String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
        //需要上傳到VOD的本地視頻檔案的完整路徑,需要包含副檔名
        String localFile = "E:/demo/demo.mp4";
        // 填寫網路流地址。可訪問的url即可
        String url = "https://bucket-name*****.oss-cn-shanghai.aliyuncs.com/demo/demo.mp4";

        try {
            // 初始化VOD用戶端並擷取上傳地址和憑證
            DefaultAcsClient vodClient = initVodClient(accessKeyId, accessKeySecret);

            //擷取視頻上傳憑證
            CreateUploadVideoResponse createUploadVideoResponse = createUploadVideo(vodClient);
            //擷取重新整理上傳憑證
            //RefreshUploadVideoResponse refreshUploadVideoResponse = refreshUploadVideo(vodClient);
            //擷取圖片上傳憑證
            //CreateUploadImageResponse createUploadImageResponse = createUploadImage(vodClient);
            //擷取輔助媒資上傳憑證
            //CreateUploadAttachedMediaResponse createUploadAttachedMediaResponse = createUploadAttachedMedia(vodClient);

            // 執行成功會返回VideoId、UploadAddress和UploadAuth
            String videoId = createUploadVideoResponse.getVideoId();
            JSONObject uploadAuth = JSONObject.parseObject(decodeBase64(createUploadVideoResponse.getUploadAuth()));
            JSONObject uploadAddress = JSONObject.parseObject(decodeBase64(createUploadVideoResponse.getUploadAddress()));

            //使用UploadAuth和UploadAddress初始化OSS用戶端
           // 通過這個OSSClient對象,傳入uploadAuth和uploadAddress,即可完成視頻檔案到阿里雲OSS的上傳
            OSSClient ossClient = initOssClient(uploadAuth, uploadAddress);

            // 上傳方式, 按需選擇
            // 1.本地上傳檔案,注意是同步上傳會阻塞等待,耗時與檔案大小和網路上行頻寬有關
            uploadLocalFile(ossClient, uploadAddress, localFile);
            // 2.網路檔案上傳
            //uploadURLFile(ossClient, uploadAddress, url);
            // 3.分區、斷點續傳
            //uploadEnableCheckPointFile(ossClient, uploadAddress, localFile);
            // 4.上傳m3u8
            //String indexFile = "E:/demo/demo.m3u8";
            //String[] tsFiles = {"E:/demo/demo_01.ts"};
            //uploadLocalM3U8(ossClient, uploadAddress, indexFile, tsFiles);

            System.out.println("Put local file succeed, VideoId : " + videoId);
        } catch (Exception e) {
            System.out.println("Put local file fail, ErrorMessage : " + e.getLocalizedMessage());
        }
    }

    /**
     * 初始化VOD用戶端
     * @throws ClientException
     */
    public static DefaultAcsClient initVodClient(String accessKeyId, String accessKeySecret) throws ClientException {
        // 點播服務接入地區,中國內地請填cn-shanghai,其他地區請參考文檔 https://www.alibabacloud.com/help/apsaravideo-for-vod/latest/vod-centers-and-endpoints
        String regionId = "cn-shanghai";
        DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
        DefaultAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    /**
     * 初始化OSS用戶端
     * @throws ClientException
     */
    public static OSSClient initOssClient(JSONObject uploadAuth, JSONObject uploadAddress) {
        String endpoint = uploadAddress.getString("Endpoint");
        String accessKeyId = uploadAuth.getString("AccessKeyId");
        String accessKeySecret = uploadAuth.getString("AccessKeySecret");
        String securityToken = uploadAuth.getString("SecurityToken");
        return new OSSClient(endpoint, accessKeyId, accessKeySecret, securityToken);
    }

    /**
     * 擷取上傳憑證
     * @throws ClientException
     */
    public static CreateUploadVideoResponse createUploadVideo(DefaultAcsClient vodClient) throws ClientException {
        CreateUploadVideoRequest request = new CreateUploadVideoRequest();
        request.setFileName("vod_test.m3u8");
        request.setTitle("this is title");
        //request.setDescription("this is desc");
        //request.setTags("tag1,tag2");
        //request.setCoverURL("http://vod.****.com/test_cover_url.jpg");
        //request.setCateId(-1L);
        //request.setTemplateGroupId("34f055******c7af499c73");
        //request.setWorkflowId("");
        //request.setStorageLocation("");
        //request.setAppId("app-1000000");

        return vodClient.getAcsResponse(request);
    }

    /**
     * 擷取重新整理憑證
     * @throws ClientException
     */
    public static RefreshUploadVideoResponse refreshUploadVideo(DefaultAcsClient vodClient) throws ClientException {
        RefreshUploadVideoRequest request = new RefreshUploadVideoRequest();
        request.setAcceptFormat(FormatType.JSON);
        request.setVideoId("VideoId");

        //佈建要求逾時時間
        request.setSysReadTimeout(1000);
        request.setSysConnectTimeout(1000);

        return vodClient.getAcsResponse(request);
    }

    /**
     * 擷取圖片上傳憑證
     * @throws ClientException
     */
    public static CreateUploadImageResponse createUploadImage(DefaultAcsClient vodClient) throws ClientException {
        CreateUploadImageRequest request = new CreateUploadImageRequest();
        request.setImageType("default");
        request.setAcceptFormat(FormatType.JSON);

        //佈建要求逾時時間
        request.setSysReadTimeout(1000);
        request.setSysConnectTimeout(1000);

        return vodClient.getAcsResponse(request);
    }

    /**
     * 擷取輔助媒資上傳憑證
     * @throws ClientException
     */
    public static CreateUploadAttachedMediaResponse createUploadAttachedMedia(DefaultAcsClient vodClient) throws ClientException {
        CreateUploadAttachedMediaRequest request = new CreateUploadAttachedMediaRequest();
        request.setBusinessType("watermark");
        request.setMediaExt("png");
        request.setAcceptFormat(FormatType.JSON);

        //佈建要求逾時時間
        request.setSysReadTimeout(1000);
        request.setSysConnectTimeout(1000);

        return vodClient.getAcsResponse(request);
    }

    /**
     *  本地檔案上傳
     * @throws Exception
     */
    public static void uploadLocalFile(OSSClient ossClient, JSONObject uploadAddress, String localFile) {
        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");

        try {
            File file = new File(localFile);
            PutObjectResult result = ossClient.putObject(bucketName, objectName, file);
            // 如果上傳成功,則返回200。
            // 上傳檔案的同時指定進度條參數。此處UploadOss為調用類的類名,請在實際使用時替換為相應的類名。
//            PutObjectResult result = ossClient.putObject(new PutObjectRequest(bucketName,objectName, file).
//                    <PutObjectRequest>withProgressListener(new UploadOss()));
//            System.out.println(result.getResponse().getStatusCode());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 關閉OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     *  網路流式上傳
     * @throws Exception
     */
    public static void uploadURLFile(OSSClient ossClient, JSONObject uploadAddress, String url) {
        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");

        try {
            InputStream inputStream = new URL(url).openStream();
            PutObjectResult result = ossClient.putObject(bucketName, objectName, inputStream);
            // 如果上傳成功,則返回200。
            System.out.println(result.getResponse().getStatusCode());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 關閉OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     *  斷點續傳上傳
     * @throws Exception
     */
    public static void uploadEnableCheckPointFile(OSSClient ossClient, JSONObject uploadAddress, String localFile) {
        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");

        try {
            UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName,objectName);
            // 通過UploadFileRequest設定單個參數。
            // 填寫本地檔案的完整路徑,例如D:\\localpath\\examplefile.mp4。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
            uploadFileRequest.setUploadFile(localFile);
            // 指定上傳並發線程數,預設值為1。
            uploadFileRequest.setTaskNum(5);
            // 指定上傳的分區大小,單位為位元組,取值範圍為100 KB~5 GB。預設值為100 KB。
            uploadFileRequest.setPartSize(1 * 1024 * 1024);
            // 開啟斷點續傳,預設關閉。
            uploadFileRequest.setEnableCheckpoint(true);
            // 記錄本地分區上傳結果的檔案。上傳過程中的進度資訊會儲存在該檔案中,如果某一分區上傳失敗,再次上傳時會根據檔案中記錄的點繼續上傳。上傳完成後,該檔案會被刪除。
            // 如果未設定該值,預設與待上傳的本地檔案同路徑,名稱為${uploadFile}.ucp。
            //uploadFileRequest.setCheckpointFile("yourCheckpointFile");
            // 斷點續傳上傳。
            ossClient.uploadFile(uploadFileRequest);

        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 關閉OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     *  本地上傳m3u8
     * @throws Exception
     */
    public static void uploadLocalM3U8(OSSClient ossClient, JSONObject uploadAddress, String indexFile, String[] tsFiles ) {

        String bucketName = uploadAddress.getString("Bucket");
        String objectName = uploadAddress.getString("FileName");
        String objectPrefix = uploadAddress.getString("ObjectPrefix");
        System.out.println(uploadAddress.toJSONString());

        try {
            //上傳索引檔案indexFile
            File index = new File(indexFile);
            ossClient.putObject(bucketName, objectName, index);
            Thread.sleep(200);
            //上傳ts檔案  tsFiles
            for(String filePath : tsFiles){
                File ts = new File(filePath);
                ossClient.putObject(bucketName, objectPrefix + ts.getName(), ts);
                Thread.sleep(200);
            }
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (Throwable ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            // 關閉OSSClient。
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    // 使用進度條需要您重寫回調方法,以下僅為參考樣本。
    @Override
    public void progressChanged(ProgressEvent progressEvent) {
        long bytes = progressEvent.getBytes();
        ProgressEventType eventType = progressEvent.getEventType();
        switch (eventType) {
            case TRANSFER_STARTED_EVENT:
                System.out.println("Start to upload......");
                break;
            case REQUEST_CONTENT_LENGTH_EVENT:
                this.totalBytes = bytes;
                System.out.println(this.totalBytes + " bytes in total will be uploaded to OSS");
                break;
            case REQUEST_BYTE_TRANSFER_EVENT:
                this.bytesWritten += bytes;
                if (this.totalBytes != -1) {
                    int percent = (int)(this.bytesWritten * 100.0 / this.totalBytes);
                    System.out.println(bytes + " bytes have been written at this time, upload progress: " + percent + "%(" + this.bytesWritten + "/" + this.totalBytes + ")");
                } else {
                    System.out.println(bytes + " bytes have been written at this time, upload ratio: unknown" + "(" + this.bytesWritten + "/...)");
                }
                break;
            case TRANSFER_COMPLETED_EVENT:
                this.succeed = true;
                System.out.println("Succeed to upload, " + this.bytesWritten + " bytes have been transferred in total");
                break;
            case TRANSFER_FAILED_EVENT:
                System.out.println("Failed to upload, " + this.bytesWritten + " bytes have been transferred");
                break;
            default:
                break;
        }
    }

    public static String decodeBase64(String s) {
        byte[] b = null;
        String result = null;
        if (s != null) {
            Base64 decoder = new Base64();
            try {
                b = decoder.decode(s);
                result = new String(b, "utf-8");
            } catch (Exception e) {
            }
        }
        return result;
    }
}