本文介紹如何使用Go SDK V2新增的File-Like接口訪問存儲空間的對象。
注意事項
本文示例代碼以華東1(杭州)的地域ID
cn-hangzhou
為例,默認使用外網Endpoint,如果您希望通過與OSS同地域的其他阿里云產品訪問OSS,請使用內網Endpoint。關于OSS支持的Region與Endpoint的對應關系,請參見OSS地域和訪問域名。本文以從環境變量讀取訪問憑證為例。如何配置訪問憑證,請參見配置訪問憑證。
要進行文件下載,您必須有
oss:GetObject
權限。具體操作,請參見為RAM用戶授權自定義的權限策略。
方法定義
Go SDK V2新增了File-Like接口,以只讀文件(ReadOnlyFile)的方式訪問存儲空間的對象。
提供了單流和并發+預取兩種模式,您可以根據場景需要調整并發數,以提升讀取速度。
接口內部實現了連接斷掉重連的機制,在一些比較復雜的網絡環境下,具備更好的魯棒性。
type ReadOnlyFile struct {
...
}
func (c *Client) OpenFile(ctx context.Context, bucket string, key string, optFns ...func(*OpenOptions)) (file *ReadOnlyFile, err error)
請求參數列表
參數名 | 類型 | 說明 |
ctx | context.Context | 請求的上下文 |
bucket | string | 設置存儲空間名稱 |
key | string | 設置對象名 |
optFns | ...func(*OpenOptions) | (可選)打開文件時的配置選項 |
其中,OpenOptions選項說明列舉如下:
參數名 | 類型 | 說明 |
Offset | int64 | 打開文件時的初始偏移量,默認值是0 |
VersionId | *string | 指定對象的版本號,多版本下有效 |
RequestPayer | *string | 啟用了請求者付費模式時,需要設置為'requester' |
EnablePrefetch | bool | 是否啟用預取模式,默認不啟用 |
PrefetchNum | int | 預取塊的數量,默認值為3。啟用預取模式時有效 |
ChunkSize | int64 | 每個預取塊的大小,默認值為6MiB。啟用預取模式時有效 |
PrefetchThreshold | int64 | 持續順序讀取多少字節后進入到預取模式,默認值為20MiB。啟用預取模式時有效 |
返回值列表
返回值名 | 類型 | 說明 |
file | *ReadOnlyFile | 只讀文件的實例,當 err 為nil 時有效,具體請參見ReadOnlyFile |
err | error | 打開只讀文件的狀態,當失敗時,err 不為 nil |
其中,ReadOnlyFile接口的常用方法列舉如下:
方法名 | 說明 |
Close() error | 關閉文件句柄,釋放資源,例如內存,活動的socket等 |
Read(p []byte) (int, error) | 從數據源中讀取長度為len(p)的字節,存儲到p中,返回讀取的字節數和遇到的錯誤 |
Seek(offset int64, whence int) (int64, error) | 用于設置下一次讀或寫的偏移量。其中whence的取值:0:相對于頭部,1:相對于當前偏移量,2:相對于尾部 |
Stat() (os.FileInfo, error) | 獲取對象的信息,包括 對象大小,最后修改時間 以及元信息 |
注意:當預取模式打開時,如果出現多次亂序讀時,則會自動退回單流模式。
示例代碼
以單流模式讀取整個對象
package main
import (
"context"
"flag"
"io"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
// 定義全局變量
var (
region string // 存儲區域
bucketName string // 存儲空間名稱
objectName string // 對象名稱
)
// init函數用于初始化命令行參數
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
// 解析命令行參數
flag.Parse()
// 檢查bucket名稱是否為空
if len(bucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, bucket name required")
}
// 檢查region是否為空
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 檢查object名稱是否為空
if len(objectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, object name required")
}
// 加載默認配置并設置憑證提供者和區域
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// 創建OSS客戶端
client := oss.NewClient(cfg)
// 打開文件
f, err := client.OpenFile(context.TODO(), bucketName, objectName)
if err != nil {
log.Fatalf("failed to open file %v", err)
}
defer f.Close()
// 使用io.Copy讀取對象內容
written, err := io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to read file %v", err)
}
log.Printf("read %d bytes from file", written)
}
啟用預取模式讀取整個對象
package main
import (
"context"
"flag"
"io"
"log"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
// 定義全局變量
var (
region string // 存儲區域
bucketName string // 存儲空間名稱
objectName string // 對象名稱
)
// init函數用于初始化命令行參數
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
// 解析命令行參數
flag.Parse()
// 檢查bucket名稱是否為空
if len(bucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, bucket name required")
}
// 檢查region是否為空
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 檢查object名稱是否為空
if len(objectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, object name required")
}
// 加載默認配置并設置憑證提供者和區域
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// 創建OSS客戶端
client := oss.NewClient(cfg)
// 打開文件
f, err := client.OpenFile(context.TODO(),
bucketName,
objectName,
func(oo *oss.OpenOptions) {
oo.EnablePrefetch = true // 啟用預取模式
})
if err != nil {
log.Fatalf("failed to open file %v", err)
}
defer f.Close()
// 讀取文件
written, err := io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to read file %v", err)
}
log.Printf("read %d bytes from file", written)
}
通過Seek方法從指定位置開始讀取剩余的數據
package main
import (
"context"
"flag"
"io"
"log"
"net/http"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
// 定義全局變量
var (
region string // 存儲區域
bucketName string // 存儲空間名稱
objectName string // 對象名稱
)
// init函數用于初始化命令行參數
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
flag.StringVar(&objectName, "object", "", "The name of the object.")
}
func main() {
// 解析命令行參數
flag.Parse()
// 檢查bucket名稱是否為空
if len(bucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, bucket name required")
}
// 檢查region是否為空
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 檢查object名稱是否為空
if len(objectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, object name required")
}
// 加載默認配置并設置憑證提供者和區域
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// 創建OSS客戶端
client := oss.NewClient(cfg)
// 打開文件
f, err := client.OpenFile(context.TODO(), bucketName, objectName)
if err != nil {
log.Fatalf("failed to open file %v", err)
}
defer f.Close()
// 獲取對象信息
info, _ := f.Stat()
// 打印對象的基本屬性
log.Printf("size:%v, mtime:%v\n", info.Size(), info.ModTime())
// 對象元數據
if header, ok := info.Sys().(http.Header); ok {
log.Printf("content-type:%v\n", header.Get(oss.HTTPHeaderContentType))
}
// 設置讀取文件的偏移值,例如從123開始讀取
_, err = f.Seek(123, io.SeekStart)
if err != nil {
log.Fatalf("failed to seek file %v", err)
}
// 使用io.Copy讀取文件
written, err := io.Copy(io.Discard, f)
if err != nil {
log.Fatalf("failed to read file %v", err)
}
log.Printf("read %d bytes from file", written)
}