Go类文件只读

本文介绍如何使用Go SDK V2新增的File-Like接口访问存储空间的对象。

注意事项

  • 本文示例代码以华东1(杭州)的地域IDcn-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(&region, "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(&region, "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(&region, "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)
}

相关文档

  • 关于类文件只读的更多信息,请参见开发者指南

  • 关于类文件只读的完整示例,请参见Github示例

  • 关于类文件只读的API接口,请参见OpenFile