更新时间: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"` }
父主题: 分段上传代码示例