更新时间:2024-10-24 GMT+08:00

Java

分段上传Java语言的示例代码,如下所示:

/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2022. All rights reserved.
 */
package com.huaweicloud.vod;

import com.huaweicloud.sdk.core.auth.BasicCredentials;
import com.huaweicloud.sdk.core.auth.ICredential;
import com.huaweicloud.sdk.core.exception.ConnectionException;
import com.huaweicloud.sdk.core.exception.RequestTimeoutException;
import com.huaweicloud.sdk.core.exception.ServiceResponseException;
import com.huaweicloud.sdk.vod.v1.VodClient;
import com.huaweicloud.sdk.vod.v1.model.ConfirmAssetUploadReq;
import com.huaweicloud.sdk.vod.v1.model.ConfirmAssetUploadRequest;
import com.huaweicloud.sdk.vod.v1.model.ConfirmAssetUploadResponse;
import com.huaweicloud.sdk.vod.v1.model.CreateAssetByFileUploadReq;
import com.huaweicloud.sdk.vod.v1.model.CreateAssetByFileUploadRequest;
import com.huaweicloud.sdk.vod.v1.model.CreateAssetByFileUploadResponse;
import com.huaweicloud.sdk.vod.v1.model.ShowAssetTempAuthorityRequest;
import com.huaweicloud.sdk.vod.v1.model.ShowAssetTempAuthorityResponse;
import com.huaweicloud.sdk.vod.v1.region.VodRegion;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.List;
import java.util.TimeZone;

/**
 * 分段上传示例
 */
public class PartUploadDemo {

    // 设置缓冲区大小,每次读取文件分段的大小,根据情况自行设定
    public static final int bufferSize = 1024 * 1024; // 1MB

    // 区域
    public static final String REGION_NORTH4 = "cn-north-4";
    public static final String REGION_NORTH1 = "cn-north-1";
    public static final String REGION_EAST2 = "cn-east-2";

    // ak/sk
    private static final String AK = "";
    private static final String SK = "";

    public static void main(String[] args) {
        // 本地要上传的媒资路径
        String filePath = "";

        PartUploadDemo uploadDemo = new PartUploadDemo();
        // 上传媒资文件
        uploadDemo.uploadPartFile(filePath);
    }

    /**
     * 分段上传
     * @param filePath 上传文件的本地路径
     */
    public void uploadPartFile(String filePath) {
        // 校验一下文件路径和文件
        File file = validFile(filePath);
        try {
            String fileName = file.getName();

            // 此处仅以MP4文件示例,其他格式可参考官网说明
            String fileType = "MP4";
            String fileContentType = "video/mp4";

            // FileInfo fileInfo = new FileInfo(filePath, file);
            // 1.初始化鉴权并获取vodClient
            VodClient vodClient = this.createVodClient();
            System.out.println("开始创建媒资:" + file.getName());
            // 2.创建点播媒资
            CreateAssetByFileUploadReq reqbody = this.buildFileUploadReq(fileName, fileType, null, null);
            CreateAssetByFileUploadResponse assetResponse = this.createAssetByFileUpload(vodClient, reqbody);
            // 3.获取初始化上传任务授权
            ShowAssetTempAuthorityResponse initAuthResponse = this.initPartUploadAuthority(vodClient, assetResponse, fileContentType);
            // 4.初始化上传任务
            String uploadId = this.initPartUpload(initAuthResponse.getSignStr(), fileContentType);

            // 文件分段计数
            int partNumber = 1;
            // 缓冲区
            byte[] fileByte = new byte[bufferSize];
            // 记录读取的长度
            int readLength = 0;

            // 7.读取文件内容, 循环5-6步上传所有分段
            FileInputStream fis = new FileInputStream(file);
            // MD5
            MessageDigest md = MessageDigest.getInstance("MD5");
            while ((readLength = fis.read(fileByte)) > 0) {
                // 读取的长度小于缓冲区长度,复制有效长度内数据,用于最后一段
                if(readLength < bufferSize) {
                    fileByte = Arrays.copyOf(fileByte, readLength);
                }
                // 先md5,再base64 encode
                byte[] digest = md.digest(fileByte);
                String contentMd5 = new String(Base64.encodeBase64(digest));
                System.out.println("该文件第"+(partNumber)+"段MD5为: " + contentMd5);

                // 5.获取上传分段的授权
                ShowAssetTempAuthorityResponse partUploadAuthorityResponse = this.getPartUploadAuthority(vodClient, fileContentType, assetResponse, contentMd5, uploadId, partNumber);

                // 6.上传分段
                this.uploadPartFile(partUploadAuthorityResponse.getSignStr(), fileByte, contentMd5);

                // 段号自增
                partNumber++;
            }

            fis.close();

            // 8.获取已上传分段的授权
            ShowAssetTempAuthorityResponse listPartUploadAuthorityResponse = this.listUploadedPartAuthority(vodClient, assetResponse, uploadId);
            // 9.获取已上传的分段
            String partInfo = this.listUploadedPart(listPartUploadAuthorityResponse.getSignStr());
            // 10.获取合并段授权
            ShowAssetTempAuthorityResponse mergePartUploadAuthorityResponse = this.mergeUploadedPartAuthority(vodClient, assetResponse, uploadId);
            // 11.合并上传分段
            this.mergeUploadedPart(mergePartUploadAuthorityResponse.getSignStr(), partInfo);
            // 12.确认媒资上传
            this.confirmUploaded(vodClient, assetResponse);
            System.out.println("创建媒资结束 assetId:" + assetResponse.getAssetId());
        } catch (ServiceResponseException e) {
            e.printStackTrace();
            System.out.println(String.valueOf(e.getHttpStatusCode()));
            System.out.println(e.getRequestId());
            System.out.println(e.getErrorCode());
            System.out.println(e.getErrorMsg());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 校验文件路径和文件
     * @param filePath
     */
    private File validFile(String filePath){
        if (StringUtils.isEmpty(filePath)) {
            throw new RuntimeException("输入的文件路径为空!");
        }
        File file = new File(filePath);
        if (!file.exists()) {
            throw new RuntimeException("文件不存在!");
        } else if (file.isDirectory()) {
            try {
                throw new RuntimeException(file.getCanonicalPath() + "这是一个目录!");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return file;
    }


    /**
     * 1.构建鉴权
     * @return
     */
    public VodClient createVodClient(){
        ICredential auth = new BasicCredentials()
            .withAk(AK)
            .withSk(SK)
            ;
        return VodClient.newBuilder()
            .withCredential(auth)
            .withRegion(VodRegion.valueOf(REGION_NORTH4))
            .build();
    }

    /**
     * 2.创建媒资
     * @param client
     * @param reqbody
     * @return
     */
    public CreateAssetByFileUploadResponse createAssetByFileUpload(VodClient client, CreateAssetByFileUploadReq reqbody){
        System.out.println("createAssetByFileUpload start");
        CreateAssetByFileUploadRequest request = new CreateAssetByFileUploadRequest();
        // 设置 X-Sdk-Date参数,ak/sk认证必传
        request.setXSdkDate(getXSdkDate());
        // 设置上传参数
        request.withBody(reqbody);

        // 调用创建媒资
        CreateAssetByFileUploadResponse response = client.createAssetByFileUpload(request);
        System.out.println("createAssetByFileUpload end; createAssetResponse:" + response.toString());
        return response;
    }

    /**
     * 构建创建媒资请求参数
     * @param fileName
     * @param videoName
     * @param title
     * @return
     */
    public CreateAssetByFileUploadReq buildFileUploadReq(String fileName, String videoType, String videoName, String title){
        CreateAssetByFileUploadReq req = new CreateAssetByFileUploadReq();
        req.withVideoName(StringUtils.isNotEmpty(videoName) ? videoName : fileName);
        req.withTitle(StringUtils.isNotEmpty(title) ? title : fileName);
        // 设置媒资类型
        req.withVideoType(videoType);
        return req;
    }

    /**
     * 取 X-Sdk-Date 参数,当前UTC时间, 时间格式 yyyyHHddTHHmmssZ 例如: 20240312T092514Z
     * @return
     */
    public String getXSdkDate(){
        TimeZone zone = TimeZone.getTimeZone("UTC");
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss'Z'");
        return LocalDateTime.now(zone.toZoneId()).format(formatter);
    }

    /**
     * 3.获取初始化上传任务授权
     * @param client
     * @param assetResponse
     */
    public ShowAssetTempAuthorityResponse initPartUploadAuthority(VodClient client, CreateAssetByFileUploadResponse assetResponse, String fileContentType){
        System.out.println("获取初始化上传任务授权 initPartUploadAuthority start");
        ShowAssetTempAuthorityRequest request = new ShowAssetTempAuthorityRequest();
        request.setXSdkDate(getXSdkDate());
        // 设置参数
        request.withHttpVerb("POST");
        request.withBucket(assetResponse.getTarget().getBucket());
        request.withObjectKey(assetResponse.getTarget().getObject());
        request.withContentType(fileContentType);
        // 发送初始化请求
        ShowAssetTempAuthorityResponse response = client.showAssetTempAuthority(request);
        System.out.println("获取初始化上传任务授权 initPartUploadAuthority end; response: " + response.toString());
        return response;
    }

    /**
     * 4.初始化分段上传
     * @param signStr
     * @param contentType
     * @return
     */
    public String initPartUpload(String signStr, String contentType) throws DocumentException {
        System.out.println("初始化分段上传 initPartUpload start");
        HttpResponse response = HttpRequest.post(signStr).header("Content-type",contentType).execute();
        System.out.println(response.body());
        Document document = DocumentHelper.parseText(response.body());
        Element root = document.getRootElement();
        Element u = root.element("UploadId");
        System.out.println("初始化分段上传 initPartUpload end; UploadId:" + u.getText());
        return u.getText();
    }

    /**
     * 5.获取分段上传授权
     * @param client
     * @param fileContentType
     * @param assetResponse
     * @param contentMd5
     * @param uploadId
     * @param partNumber
     * @return
     */
    public ShowAssetTempAuthorityResponse getPartUploadAuthority(VodClient client, String fileContentType, CreateAssetByFileUploadResponse assetResponse, String contentMd5, String uploadId, int partNumber) {
        System.out.println("获取分段上传授权 getPartUploadAuthority start; partNumber: " + partNumber);
        ShowAssetTempAuthorityRequest request = new ShowAssetTempAuthorityRequest();
        request.setXSdkDate(getXSdkDate());
        request.withHttpVerb("PUT");
        request.withBucket(assetResponse.getTarget().getBucket());
        request.withObjectKey(assetResponse.getTarget().getObject());
        request.withContentType(fileContentType);
        request.withContentMd5(contentMd5);
        request.withUploadId(uploadId);
        request.withPartNumber(partNumber);
        ShowAssetTempAuthorityResponse response = client.showAssetTempAuthority(request);
        System.out.println("获取分段上传授权 getPartUploadAuthority end; partNumber: " + partNumber + "; response" + response.toString());
        return response;
    }

    /**
     * 6.上传分段
     * @param signStr
     * @param fileByte
     * @param contentMd5
     */
    public void uploadPartFile(String signStr, byte[] fileByte, String contentMd5) {
        System.out.println("上传分段 uploadPartFile start");
        HttpResponse response = HttpRequest.put(signStr)
            // 此处contentMd5不需要转义
            .header("Content-MD5", contentMd5)
            .header("Content-Type", "application/octet-stream")
            .body(fileByte).execute();
        System.out.println(response.toString());
        if (response.getStatus() != 200) {
            throw new RuntimeException("上传分段 uploadPartFile end; 上传失败!");
        }
        System.out.println("上传分段 uploadPartFile end");
    }

    /**
     * 8.获取列举已上传段的授权
     * @param client
     * @param assetResponse
     * @param uploadId
     * @return
     */
    public ShowAssetTempAuthorityResponse listUploadedPartAuthority(VodClient client, CreateAssetByFileUploadResponse assetResponse, String uploadId){
        System.out.println("获取列举已上传段的授权 listUploadedPartAuthority start");
        ShowAssetTempAuthorityRequest request = new ShowAssetTempAuthorityRequest();
        request.setXSdkDate(getXSdkDate());
        request.withHttpVerb("GET");
        request.withBucket(assetResponse.getTarget().getBucket());
        request.withObjectKey(assetResponse.getTarget().getObject());
        request.withUploadId(uploadId);
        ShowAssetTempAuthorityResponse response = client.showAssetTempAuthority(request);
        System.out.println("获取列举已上传段的授权 listUploadedPartAuthority end; response: " + response.toString());
        return response;
    }

    /**
     * 9.查询已上传的分段
     * @param signStr
     * @return
     */
    public String listUploadedPart(String signStr) throws DocumentException {
        System.out.println("查询已上传的分段 listUploadedPart start");
        int partNumberMarker = 0;
        Element mergerRoot = DocumentHelper.createElement("CompleteMultipartUpload");
        while(true) {
            //列举分段
            HttpResponse response = HttpRequest.get(signStr + "&part-number-marker=" + partNumberMarker).execute();
            System.out.println("listUploadedPartResponse:" + response.body());
            Document responseDocument = DocumentHelper.parseText(response.body());
            Element rootResponse = responseDocument.getRootElement();
            List<Element> elementsResponse = rootResponse.elements("Part");
            Element partNumberMarkerElement = rootResponse.element("NextPartNumberMarker");
            for (Element e : elementsResponse) {
                Element te = DocumentHelper.createElement("Part");
                te.add(e.element("PartNumber").createCopy());
                te.add(e.element("ETag").createCopy());
                mergerRoot.add(te);
            }
            partNumberMarker = Integer.valueOf(partNumberMarkerElement.getText());
            if(partNumberMarker % 1000 != 0) {
                break;
            }
        }
        System.out.println(mergerRoot.asXML());
        System.out.println("查询已上传的分段 listUploadedPart end");
        return mergerRoot.asXML();
    }

    /**
     * 10.获取合并段授权
     * @param client
     * @param assetResponse
     * @param uploadId
     * @return
     */
    public ShowAssetTempAuthorityResponse mergeUploadedPartAuthority(VodClient client, CreateAssetByFileUploadResponse assetResponse, String uploadId) {
        System.out.println("获取合并段授权 mergeUploadedPartAuthority start");
        ShowAssetTempAuthorityRequest request = new ShowAssetTempAuthorityRequest();
        request.setXSdkDate(getXSdkDate());
        request.withHttpVerb("POST");
        request.withBucket(assetResponse.getTarget().getBucket());
        request.withObjectKey(assetResponse.getTarget().getObject());
        request.withUploadId(uploadId);
        ShowAssetTempAuthorityResponse response = client.showAssetTempAuthority(request);
        System.out.println("获取合并段授权 mergeUploadedPartAuthority end; response: " + response.toString());
        return response;
    }

    /**
     * 11.合并分段
     */
    public void mergeUploadedPart(String signStr, String partInfo) {
        System.out.println("合并分段 mergeUploadedPart start");
        // 请求消息头中增加“Content-Type”,值设置为“application/xml”。
        HttpResponse response = HttpRequest.post(signStr)
            .header("Content-Type","application/xml")
            .body(partInfo)
            .execute();
        System.out.println(response.toString());
        if (response.getStatus() != 200) {
            throw new RuntimeException("合并分段 mergeUploadedPart end; 合并段失败!");
        }
        System.out.println("合并分段 mergeUploadedPart end");
    }

    /**
     * 12.确认上传完成
     */
    public void confirmUploaded(VodClient client, CreateAssetByFileUploadResponse assetResponse) {
        System.out.println("确认上传完成 confirmUploaded start");
        ConfirmAssetUploadRequest request = new ConfirmAssetUploadRequest();
        request.setXSdkDate(getXSdkDate());
        ConfirmAssetUploadReq body = new ConfirmAssetUploadReq();
        body.withStatus(ConfirmAssetUploadReq.StatusEnum.fromValue("CREATED"));
        body.withAssetId(assetResponse.getAssetId());
        request.withBody(body);
        ConfirmAssetUploadResponse response = client.confirmAssetUpload(request);
        System.out.println("上传完成,assetId:" + response.toString());
    }

}