更新时间:2024-10-24 GMT+08:00
Go
分段上传Go语言的示例代码,如下所示:
package main
import (
"bytes"
"crypto/md5"
"encoding/base64"
"encoding/xml"
"errors"
"fmt"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core/auth/basic"
vod "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1/model"
"github.com/huaweicloud/huaweicloud-sdk-go-v3/services/vod/v1/region"
"io"
"net/http"
"os"
)
// 区域
const regionNorth4 = "cn-north-4"
const regionNorth1 = "cn-north-1"
const regionEast2 = "cn-east-2"
// ak/sk
const ak = ""
const sk = ""
// 设置缓冲区大小,每次读取文件分段的大小,根据情况自行设定
// 1M
const bufferSize = 1024 * 1024
/*
分段上传示例
*/
func main() {
// 本地要上传的媒资路径
filePath := ""
// 上传媒资文件
partUpload(filePath)
}
/*
分段上传
filePath 上传文件的本地路径
*/
func partUpload(filePath string) {
// 校验文件路径和文件
fileInfo := validFile(filePath)
fileName := fileInfo.Name()
fmt.Println(fileName)
file, err := os.Open(filePath)
if err != nil {
fmt.Println("Error:", err)
return
}
defer func(file *os.File) {
err := file.Close()
if err != nil {
panic(err)
}
}(file)
// 此处仅以MP4文件示例,其他格式可参考官网说明
fileType := "MP4"
fileContentType := "video/mp4"
// 1.初始化鉴权并获取vodClient
client := createVodClient()
// 2.创建点播媒资
fmt.Println("开始创建媒资:" + fileName)
assetResponse := createAsset(client, fileName, fileName, fileType)
// 3.获取初始化上传任务授权
initAuthResponse := initPartUploadAuthority(client, assetResponse, fileContentType)
// 4.初始化上传任务
uploadId := initPartUpload(initAuthResponse.SignStr, fileContentType)
// 文件分段计数
partNumber := 1
// 7.读取文件内容,循环5-6步上传所有分段
for {
buf := make([]byte, bufferSize)
n, err := file.Read(buf)
if n == 0 || err != nil || err == io.EOF {
break
}
// 先md5,再base64 encode
h := md5.New()
h.Write(buf[:n])
data := h.Sum(nil)
contentMd5 := base64.StdEncoding.EncodeToString(data)
fmt.Println("该文件第", partNumber, "段MD5为:", contentMd5)
// 5.获取上传分段的授权
partUploadAuthorityResponse := getPartUploadAuthority(client, fileContentType, assetResponse,
contentMd5, uploadId, partNumber)
// 6.上传分段
uploadPartFile(partUploadAuthorityResponse.SignStr, buf[:n], contentMd5)
// 段号自增
partNumber++
}
// 8.获取已上传分段的授权
listPartUploadAuthorityResponse := listUploadedPartAuthority(client, assetResponse, uploadId)
// 9.获取已上传的分段
partInfo := listUploadedPart(listPartUploadAuthorityResponse.SignStr)
// 10.获取合并段授权
mergePartUploadAuthorityResponse := mergeUploadedPartAuthority(client, assetResponse, uploadId)
// 11.合并上传分段
mergeUploadedPart(mergePartUploadAuthorityResponse.SignStr, partInfo)
// 12.确认媒资上传
confirmUploaded(client, assetResponse)
fmt.Println("创建媒资结束 assetId:", *assetResponse.AssetId)
}
/*
校验文件路径和文件
filePath: 文件路径
*/
func validFile(filePath string) os.FileInfo {
// 检查文件是否存在
fileInfo, err := os.Stat(filePath)
if os.IsNotExist(err) {
fmt.Println("文件不存在")
// 抛出错误
panic(errors.New("文件不存在"))
}
if err != nil {
fmt.Println("发生错误:", err)
panic(err)
}
return fileInfo
}
/*
1.构建鉴权
*/
func createVodClient() *vod.VodClient {
auth, _ := basic.NewCredentialsBuilder().
WithAk(ak).
WithSk(sk).
SafeBuild()
reg, _ := region.SafeValueOf(regionNorth4)
build, _ := vod.VodClientBuilder().
WithRegion(reg).
WithCredential(auth).
SafeBuild()
client := vod.NewVodClient(build)
return client
}
/*
2.创建媒资
videoName: 名称
title: 标题
videoType: 文件类型
*/
func createAsset(client *vod.VodClient, videoName string, title string,
videoType string) *model.CreateAssetByFileUploadResponse {
request := &model.CreateAssetByFileUploadRequest{}
request.Body = &model.CreateAssetByFileUploadReq{
VideoName: videoName,
Title: title,
VideoType: videoType,
}
response, err := client.CreateAssetByFileUpload(request)
if err == nil {
fmt.Println(response)
} else {
fmt.Println(err)
panic(err)
}
return response
}
/*
3.获取初始化上传任务授权
client
assetResponse
*/
func initPartUploadAuthority(client *vod.VodClient, assetResponse *model.CreateAssetByFileUploadResponse,
fileContentType string) *model.ShowAssetTempAuthorityResponse {
fmt.Println("获取初始化上传任务授权 initPartUploadAuthority start")
// 设置参数
request := &model.ShowAssetTempAuthorityRequest{}
request.HttpVerb = "POST"
request.Bucket = assetResponse.Target.Bucket
request.ObjectKey = assetResponse.Target.Object
request.ContentType = &fileContentType
// 发送初始化请求
response, err := client.ShowAssetTempAuthority(request)
if err == nil {
fmt.Print(response)
} else {
fmt.Println(err)
panic(err)
}
fmt.Println("获取初始化上传任务授权 initPartUploadAuthority end\n")
return response
}
/*
4.初始化分段上传
signStr 第3步返回的带签名的初始化url
contentType 媒体文件格式
return uploadId
*/
func initPartUpload(signStr *string, contentType string) string {
fmt.Println("初始化分段上传 initPartUpload start")
// 创建一个新的HTTP客户端
client := &http.Client{}
// 创建一个新的请求
req, err := http.NewRequest("POST", *signStr, nil)
req.Header.Set("Content-Type", contentType)
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
panic(err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
panic(err)
}
}(resp.Body)
// 读取响应体
body, _ := io.ReadAll(resp.Body)
// 打印响应体
fmt.Println(string(body))
// 解析响应体,返回结果为xml文本,按格式解析
var initiateMultipartUploadResult InitiateMultipartUploadResult
err = xml.Unmarshal(body, &initiateMultipartUploadResult)
if err != nil {
panic(err)
}
fmt.Printf("Bucket:%s, Key:%s, UploadId:%s\n", initiateMultipartUploadResult.Bucket,
initiateMultipartUploadResult.Key, initiateMultipartUploadResult.UploadId)
fmt.Println("初始化分段上传 initPartUpload end\n")
return initiateMultipartUploadResult.UploadId
}
/*
5.获取分段上传授权
client
fileContentType 媒体文件格式
assetResponse 创建媒资返回的结果
contentMd5 当前分段计算出的contentMd5
uploadId
partNumber 段号
return 授权结果
*/
func getPartUploadAuthority(client *vod.VodClient, fileContentType string, assetResponse *model.CreateAssetByFileUploadResponse,
contentMd5 string, uploadId string, partNumber int) *model.ShowAssetTempAuthorityResponse {
fmt.Println("获取分段上传授权 getPartUploadAuthority start; partNumber:", partNumber)
// 设置参数
request := &model.ShowAssetTempAuthorityRequest{}
request.HttpVerb = "PUT"
request.Bucket = assetResponse.Target.Bucket
request.ObjectKey = assetResponse.Target.Object
request.ContentType = &fileContentType
request.ContentMd5 = &contentMd5
request.UploadId = &uploadId
partNumberRequest := int32(partNumber)
request.PartNumber = &partNumberRequest
// 发送请求
response, err := client.ShowAssetTempAuthority(request)
if err == nil {
fmt.Print(response)
} else {
fmt.Println(err)
panic(err)
}
fmt.Println("获取分段上传授权 getPartUploadAuthority end; partNumber:", partNumber, "\n")
return response
}
/*
6.上传分段
signStr 第5步返回的带签名的上传url
fileByte 当前分段数据
contentMd5 当前分段contentMd5
*/
func uploadPartFile(signStr *string, fileByte []byte, contentMd5 string) {
fmt.Print("上传分段 uploadPartFile start")
// 创建一个新的HTTP客户端
client := &http.Client{}
// 创建一个新的请求
req, err := http.NewRequest("PUT", *signStr, bytes.NewBuffer(fileByte))
req.Header.Set("Content-MD5", contentMd5)
req.Header.Set("Content-Type", "application/octet-stream")
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
panic(err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
panic(err)
}
}(resp.Body)
// 读取响应体
body, _ := io.ReadAll(resp.Body)
// 打印响应体
fmt.Println(string(body))
if resp.StatusCode != 200 {
panic("上传文件失败!")
}
fmt.Println("上传分段 uploadPartFile end \n")
}
/*
8.获取列举已上传段的授权
client
assetResponse 创建媒资响应结果
uploadId
return
*/
func listUploadedPartAuthority(client *vod.VodClient, assetResponse *model.CreateAssetByFileUploadResponse,
uploadId string) *model.ShowAssetTempAuthorityResponse {
fmt.Println("获取列举已上传段的授权 listUploadedPartAuthority start")
// 设置参数
request := &model.ShowAssetTempAuthorityRequest{}
request.HttpVerb = "GET"
request.Bucket = assetResponse.Target.Bucket
request.ObjectKey = assetResponse.Target.Object
request.UploadId = &uploadId
// 发送请求
response, err := client.ShowAssetTempAuthority(request)
if err == nil {
fmt.Print(response)
} else {
fmt.Println(err)
panic(err)
}
fmt.Println("获取列举已上传段的授权 listUploadedPartAuthority end\n")
return response
}
/*
9.查询已上传的分段
signStr 第8步返回的带签名的查询url
return
*/
func listUploadedPart(signStr *string) string {
fmt.Println("查询已上传的分段 listUploadedPart start")
// 查询分段的起始段号
partNumberMarker := 0
// 组装合并段参数,xml格式
result := "<CompleteMultipartUpload>"
// 创建一个新的HTTP客户端
client := &http.Client{}
for {
// 创建一个新的请求
url := *signStr + "&part-number-marker=" + fmt.Sprintf("%d", partNumberMarker)
req, _ := http.NewRequest("GET", url, nil)
// 发送请求
resp, _ := client.Do(req)
// 读取响应体
body, _ := io.ReadAll(resp.Body)
// 打印响应体
fmt.Println(string(body))
// 解析响应结果,转换xml格式文本
var listPartsResult ListPartsResult
_ = xml.Unmarshal(body, &listPartsResult)
// 响应结果中没有parts跳出
if len(listPartsResult.Parts) < 1 {
break
}
// 循环parts组装Part数据
for _, part := range listPartsResult.Parts {
num := part.PartNumber
tag := part.Etag
result += "<Part>" +
"<PartNumber>" +
fmt.Sprintf("%d", num) +
"</PartNumber>" +
"<ETag>" +
tag +
"</ETag>" +
"</Part>"
}
// 响应中下个段号值设为起始段号并再次请求
partNumberMarker = listPartsResult.NextPartNumberMarker
if partNumberMarker%1000 != 0 {
break
}
}
result += "</CompleteMultipartUpload>"
fmt.Println(result)
fmt.Println("查询已上传的分段 listUploadedPart end\n")
return result
}
/*
10.获取合并段授权
client
assetResponse
uploadId
return
*/
func mergeUploadedPartAuthority(client *vod.VodClient, assetResponse *model.CreateAssetByFileUploadResponse,
uploadId string) *model.ShowAssetTempAuthorityResponse {
fmt.Println("获取合并段授权 mergeUploadedPartAuthority start")
// 设置参数
request := &model.ShowAssetTempAuthorityRequest{}
request.HttpVerb = "POST"
request.Bucket = assetResponse.Target.Bucket
request.ObjectKey = assetResponse.Target.Object
request.UploadId = &uploadId
// 发送请求
response, err := client.ShowAssetTempAuthority(request)
if err == nil {
fmt.Print(response)
} else {
fmt.Println(err)
panic(err)
}
fmt.Println("获取合并段授权 mergeUploadedPartAuthority end\n")
return response
}
/*
11.合并分段
signStr 第10步返回的带签名的合并url
partInfo 需要合并段的数据
*/
func mergeUploadedPart(signStr *string, partInfo string) {
fmt.Println("合并分段 mergeUploadedPart start")
// 创建一个新的HTTP客户端
client := &http.Client{}
// 创建一个新的请求
//req, err := http.NewRequest("POST", *signStr, bytes.NewBuffer([]byte(partInfo)))
req, err := http.NewRequest("POST", *signStr, bytes.NewBufferString(partInfo))
// 请求消息头中增加 "Content-Type":"application/xml"
req.Header.Set("Content-Type", "application/xml")
// 发送请求
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error sending request:", err)
panic(err)
}
defer func(Body io.ReadCloser) {
err := Body.Close()
if err != nil {
panic(err)
}
}(resp.Body)
// 读取响应体
body, _ := io.ReadAll(resp.Body)
// 打印响应体
fmt.Println(string(body))
fmt.Println("合并分段 mergeUploadedPart end\n")
}
/*
12.确认上传完成
*/
func confirmUploaded(client *vod.VodClient, assetResponse *model.CreateAssetByFileUploadResponse) {
fmt.Println("确认上传完成 confirmUploaded start")
// 设置请求参数
request := &model.ConfirmAssetUploadRequest{}
request.Body = &model.ConfirmAssetUploadReq{
Status: model.GetConfirmAssetUploadReqStatusEnum().CREATED,
AssetId: *assetResponse.AssetId,
}
// 发送请求
response, err := client.ConfirmAssetUpload(request)
if err == nil {
fmt.Print(response)
} else {
fmt.Println(err)
panic(err)
}
fmt.Println("上传完成, assetId:", *response.AssetId)
}
// InitiateMultipartUploadResult 初始化分段上传返回的xml结构体
type InitiateMultipartUploadResult struct {
Bucket string `xml:"Bucket"`
Key string `xml:"Key"`
UploadId string `xml:"UploadId"`
}
// ListPartsResult 查询已上传段返回的xml结构体
type ListPartsResult struct {
Bucket string `xml:"Bucket"`
Key string `xml:"Key"`
UploadId string `xml:"UploadId"`
NextPartNumberMarker int `xml:"NextPartNumberMarker"`
Parts []Part `xml:"Part"`
}
// Part 查询已上传段返回的xml listPartsResult中part结构体
type Part struct {
Etag string `xml:"ETag"`
PartNumber int `xml:"PartNumber"`
}
父主题: 分段上传代码示例