# 下载对象-进度条(Go SDK)
#### 功能说明
获取下载对象进度是指在进行对象下载操作时，实时监控并获取下载进度的功能。通过实现进度监听接口，可以获取到下载任务的开始、数据传输中、下载完成或失败等状态信息，以及已下载字节数和总字节数。
#### 接口约束
- 您必须是桶拥有者或拥有下载对象的权限，才能下载对象。建议使用IAM或桶策略进行授权，如果使用IAM则需授予obs:object:GetObject权限，如果使用桶策略则需授予GetObject权限。相关授权方式介绍可参见[OBS权限控制概述](https://support.huaweicloud.com/perms-cfg-obs/obs_40_0001.html)，配置方式详见[使用IAM自定义策略](https://support.huaweicloud.com/usermanual-obs/obs_03_0121.html)、[配置对象策略](https://support.huaweicloud.com/usermanual-obs/obs_03_0075.html)。
- OBS支持的Region与Endpoint的对应关系，详细信息请参见[地区与终端节点](https://developer.huaweicloud.com/endpoint?OBS)。
- 对于存储类别为归档存储或深度归档存储的对象，需要确认对象的状态为"已恢复"才能对其进行下载。
 
#### 方法定义
WithProgress(progressListener ProgressListener)
#### 请求参数说明
表1请求参数列表 
| **参数名称**     | **参数类型**   | **是否必选** | **描述**                                                                                                                                                      |
|:---|:---|:---|:---|
| progressListener | interface | 必选             | **参数解释** **：** 进度监听器。需要实现以下方法： ProgressChanged(event \*[表2]) |
   
 表2ProgressEvent 
| **参数名称**              | **原始值** | 说明           |
|:---|:---|:---|
| TransferStartedEvent  | 1     | 下载开始时触发。     |
| TransferDataEvent     | 2        | 数据传输过程中持续触发。 |
| TransferCompletedEvent | 3        | 下载成功完成时触发。    |
| TransferFailedEvent  | 4       | 下载失败时触发。     |
   
#### 返回结果说明
返回结果参考原有方法函数调用，参考[代码示例](https://support.huaweicloud.com/sdk-go-devg-obs/obs_33_0605.html#obs_33_0605__section11655939)。
#### 代码示例-流式下载进度监控
本示例用于流式下载到examplebucket桶中的example/objectname，并监听下载进度。
```
package main
import (
    "fmt"
    "os"
    obs "github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
)
type ObsProgressListener struct {
}
// 定义进度变更事件处理函数。
func (listener *ObsProgressListener) ProgressChanged(event *obs.ProgressEvent) {
    switch event.EventType {
    case obs.TransferStartedEvent:
        fmt.Printf("Transfer Started, ConsumedBytes: %d, TotalBytes %d.\n",
            event.ConsumedBytes, event.TotalBytes)
    case obs.TransferDataEvent:
        fmt.Printf("\rTransfer Data, ConsumedBytes: %d, TotalBytes %d, %d%%.\n",
            event.ConsumedBytes, event.TotalBytes, event.ConsumedBytes*100/event.TotalBytes)
    case obs.TransferCompletedEvent:
        fmt.Printf("\nTransfer Completed, ConsumedBytes: %d, TotalBytes %d.\n",
            event.ConsumedBytes, event.TotalBytes)
    case obs.TransferFailedEvent:
        fmt.Printf("\nTransfer Failed, ConsumedBytes: %d, TotalBytes %d.\n",
            event.ConsumedBytes, event.TotalBytes)
    default:
    }
}
func main() {
    //推荐通过环境变量获取AKSK，这里也可以使用其他外部引入方式传入，如果使用硬编码可能会存在泄露风险。
    //您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html。
    ak := os.Getenv("AccessKeyID")
    sk := os.Getenv("SecretAccessKey")
    // 【可选】如果使用临时AK/SK和SecurityToken访问OBS，同样建议您尽量避免使用硬编码，以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK，也可以使用其他外部引入方式传入。
    // securityToken := os.Getenv("SecurityToken")
   // endpoint填写Bucket对应的Endpoint， 这里以华北-北京四为例，其他地区请按实际情况填写。
    endPoint := "https://obs.cn-north-4.myhuaweicloud.com"
    // 创建obsClient实例
    // 如果使用临时AKSK和SecurityToken访问OBS，需要在创建实例时通过obs.WithSecurityToken方法指定securityToken值。
    obsClient, err := obs.New(ak, sk, endPoint/*, obs.WithSecurityToken(securityToken)*/)
    if err != nil {
        fmt.Printf("Create obsClient error, errMsg: %s", err.Error())
    }
    input := &obs.GetObjectInput{}
    // 指定存储桶名称
    input.Bucket = "examplebucket"
    // 指定下载对象，此处以 example/objectname 为例。
    input.Key = "example/objectname"
    // 流式下载对象
    output, err := obsClient.GetObject(input, obs.WithProgress(&ObsProgressListener{}))
    if err == nil {
        // output.Body 在使用完毕后必须关闭，否则会造成连接泄漏。
        defer output.Body.Close()
        fmt.Printf("Get object(%s) under the bucket(%s) successful!\n", input.Key, input.Bucket)
        fmt.Printf("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n",
            output.StorageClass, output.ETag, output.ContentType, output.ContentLength, output.LastModified)
        // 读取对象内容
        p := make([]byte, 1024)
        var readErr error
        var readCount int
        for {
            readCount, readErr = output.Body.Read(p)
            if readCount > 0 {
                fmt.Printf("%s", p[:readCount])
            }
            if readErr != nil {
                break
            }
        }
        return
    }
    fmt.Printf("List objects under the bucket(%s) fail!\n", input.Bucket)
    if obsError, ok := err.(obs.ObsError); ok {
        fmt.Println("An ObsError was found, which means your request sent to OBS was rejected with an error response.")
        fmt.Println(obsError.Error())
    } else {
        fmt.Println("An Exception was found, which means the client encountered an internal problem when attempting to communicate with OBS, for example, the client was unable to access the network.")
        fmt.Println(err)
    }
}
```
![](https://support.huaweicloud.com/sdk-go-devg-obs/public_sys-resources/notice_3.0-zh-cn.png)
GetObjectOutput.Body获取的可读流一定要显式关闭，否则会造成资源泄露。
#### 代码示例-断点续传进度监控
本示例用于断点下载到examplebucket桶中的example/objectname，并监听下载进度。
```
package main
import (
    "fmt"
    "os"
    obs "github.com/huaweicloud/huaweicloud-sdk-go-obs/obs"
)
type ObsProgressListener struct {
}
// 定义进度变更事件处理函数。
func (listener *ObsProgressListener) ProgressChanged(event *obs.ProgressEvent) {
    switch event.EventType {
    case obs.TransferStartedEvent:
        fmt.Printf("Transfer Started, ConsumedBytes: %d, TotalBytes %d.\n",
            event.ConsumedBytes, event.TotalBytes)
    case obs.TransferDataEvent:
        fmt.Printf("\rTransfer Data, ConsumedBytes: %d, TotalBytes %d, %d%%.\n",
            event.ConsumedBytes, event.TotalBytes, event.ConsumedBytes*100/event.TotalBytes)
    case obs.TransferCompletedEvent:
        fmt.Printf("\nTransfer Completed, ConsumedBytes: %d, TotalBytes %d.\n",
            event.ConsumedBytes, event.TotalBytes)
    case obs.TransferFailedEvent:
        fmt.Printf("\nTransfer Failed, ConsumedBytes: %d, TotalBytes %d.\n",
            event.ConsumedBytes, event.TotalBytes)
    default:
    }
}
func main() {
    //推荐通过环境变量获取AKSK，这里也可以使用其他外部引入方式传入，如果使用硬编码可能会存在泄露风险。
    //您可以登录访问管理控制台获取访问密钥AK/SK，获取方式请参见https://support.huaweicloud.com/usermanual-ca/ca_01_0003.html。
    ak := os.Getenv("AccessKeyID")
    sk := os.Getenv("SecretAccessKey")
    // 【可选】如果使用临时AK/SK和SecurityToken访问OBS，同样建议您尽量避免使用硬编码，以降低信息泄露风险。您可以通过环境变量获取访问密钥AK/SK，也可以使用其他外部引入方式传入。
    // securityToken := os.Getenv("SecurityToken")
   // endpoint填写Bucket对应的Endpoint， 这里以华北-北京四为例，其他地区请按实际情况填写。
    endPoint := "https://obs.cn-north-4.myhuaweicloud.com"
    // 创建obsClient实例
    // 如果使用临时AKSK和SecurityToken访问OBS，需要在创建实例时通过obs.WithSecurityToken方法指定securityToken值。
    obsClient, err := obs.New(ak, sk, endPoint/*, obs.WithSecurityToken(securityToken)*/)
    if err != nil {
        fmt.Printf("Create obsClient error, errMsg: %s", err.Error())
    }
    input := &obs.DownloadFileInput{}
    // 指定存储桶名称
    input.Bucket = "examplebucket"
    // 指定下载对象，此处以 example/objectname 为例。
    input.Key = "example/objectname"
    // 指定下载对象的本地文件全路径，此处以/tmp/objectname为例。当该值为空时，默认为当前程序的运行目录。
    input.DownloadFile = "/tmp/objectname"
    // 指定是否开启断点续传模式，此处以true为例。默认为False，表示不开启。
    input.EnableCheckpoint = true
    // 指定分段大小，单位字节。此处以每段9M为例。
    input.PartSize = 9 * 1024 * 1024
    // 指定分段下载时的最大并发数，此处以并发数5为例
    input.TaskNum = 5
    // 断点续传下载对象
    output, err := obsClient.DownloadFile(input, obs.WithProgress(&ObsProgressListener{}))
    if err == nil {
        fmt.Printf("Download file(%s) under the bucket(%s) successful!\n", input.Key, input.Bucket)
        fmt.Printf("StorageClass:%s, ETag:%s, ContentType:%s, ContentLength:%d, LastModified:%s\n",
            output.StorageClass, output.ETag, output.ContentType, output.ContentLength, output.LastModified)
        return
    }
    fmt.Printf("Download file(%s) under the bucket(%s) fail!\n", input.Key, input.Bucket)
    if obsError, ok := err.(obs.ObsError); ok {
        fmt.Println("An ObsError was found, which means your request sent to OBS was rejected with an error response.")
        fmt.Println(obsError.Error())
    } else {
        fmt.Println("An Exception was found, which means the client encountered an internal problem when attempting to communicate with OBS, for example, the client was unable to access the network.")
        fmt.Println(err)
    }
}
```
#### 相关链接
- 关于下载对象的API说明，请参见[获取对象内容](https://support.huaweicloud.com/api-obs/obs_04_0083.html)。
- 下载对象过程中返回的错误码含义、问题原因及处理措施可参考[OBS错误码](https://support.huaweicloud.com/api-obs/obs_04_0115.html#section1)。
- 下载对象常见问题请参见[下载对象失败](https://support.huaweicloud.com/obs_faq/obs_faq_0135.html)。
 
