oss/lib/append_file.go (243 lines of code) (raw):
package lib
import (
"fmt"
"os"
"strconv"
"strings"
"time"
oss "github.com/aliyun/aliyun-oss-go-sdk/oss"
)
var specChineseAppendFile = SpecText{
synopsisText: "将本地文件内容以append上传方式上传到oss中的appendable object中",
paramText: "local_file_name oss_object [options]",
syntaxText: `
ossutil appendfromfile local_file_name oss://bucket/object [options]
`,
detailHelpText: `
1) 如果object不存在,可以通过--meta设置object的meta信息,比如输入 --meta "x-oss-meta-author:luxun"
可以设置x-oss-meta-author的值为luxun
2) 如果object已经存在,不可以输入--meta信息,因为oss不支持在已经存在的append object上设置meta
用法:
该命令只有一种用法:
1) ossutil appendfromfile local_file_name oss://bucket/object [--meta=meta-value]
将local_file_name内容以append方式上传到可追加的object
如果输入--meta选项,可以设置object的meta信息
`,
sampleText: `
1) append上传文件内容,不设置meta信息
ossutil appendfromfile local_file_name oss://bucket/object
2) append上传文件内容,设置meta信息
ossutil appendfromfile local_file_name oss://bucket/object --meta "x-oss-meta-author:luxun"
3) 以访问者付费模式上传文件内容
ossutil appendfromfile local_file_name oss://bucket/object --payer requester
`,
}
var specEnglishAppendFile = SpecText{
synopsisText: "Upload the contents of the local file to the oss appendable object by append upload mode",
paramText: "local_file_name oss_object [options]",
syntaxText: `
ossutil appendfromfile local_file_name oss://bucket/object [options]
`,
detailHelpText: `
1) If the object does not exist, you can set the meta information of the object with --meta
for example:
inputting --meta "x-oss-meta-author:luxun" can set the value of x-oss-meta-author to luxun
2) If the object already exists, you can't input the --meta option,
oss does not support setting the meta on the exist appendable object.
Usages:
There is only one usage for this command::
1) ossutil appendfromfile local_file_name oss://bucket/object [--meta=meta-value]
Upload the local_file_name content to the object by append mode
If you input the --meta option, you can set the meta value of the object
`,
sampleText: `
1) Uploads file content by append mode without setting meta value
ossutil appendfromfile local_file_name oss://bucket/object
2) Uploads file content by append mode with setting meta value
ossutil appendfromfile local_file_name oss://bucket/object --meta "x-oss-meta-author:luxun"
3) Uploads file content with requester payment mode
ossutil appendfromfile local_file_name oss://bucket/object --payer requester
`,
}
type AppendProgressListener struct {
lastMilliSecond int64
lastSize int64
currSize int64
}
// ProgressChanged handle progress event
func (l *AppendProgressListener) ProgressChanged(event *oss.ProgressEvent) {
if event.EventType == oss.TransferDataEvent || event.EventType == oss.TransferCompletedEvent {
if l.lastMilliSecond == 0 {
l.lastSize = l.currSize
l.currSize = event.ConsumedBytes
l.lastMilliSecond = time.Now().UnixNano() / 1000 / 1000
} else {
now := time.Now()
cost := now.UnixNano()/1000/1000 - l.lastMilliSecond
if cost > 1000 || event.EventType == oss.TransferCompletedEvent {
l.lastSize = l.currSize
l.currSize = event.ConsumedBytes
l.lastMilliSecond = now.UnixNano() / 1000 / 1000
speed := (float64(l.currSize-l.lastSize) / 1024) / (float64(cost) / 1000)
rate := float64(l.currSize) * 100 / float64(event.TotalBytes)
fmt.Printf("\rtotal append %d(%.2f%%) byte,speed is %.2f(KB/s)", event.ConsumedBytes, rate, speed)
}
}
}
}
type appendFileOptionType struct {
bucketName string
objectName string
encodingType string
fileName string
fileSize int64
ossMeta string
}
type AppendFileCommand struct {
command Command
afOption appendFileOptionType
commonOptions []oss.Option
}
var appendFileCommand = AppendFileCommand{
command: Command{
name: "appendfromfile",
nameAlias: []string{"appendfromfile"},
minArgc: 2,
maxArgc: 2,
specChinese: specChineseAppendFile,
specEnglish: specEnglishAppendFile,
group: GroupTypeNormalCommand,
validOptionNames: []string{
OptionConfigFile,
OptionEndpoint,
OptionAccessKeyID,
OptionAccessKeySecret,
OptionSTSToken,
OptionProxyHost,
OptionProxyUser,
OptionProxyPwd,
OptionEncodingType,
OptionMeta,
OptionMaxUpSpeed,
OptionLogLevel,
OptionRequestPayer,
OptionPassword,
OptionMode,
OptionECSRoleName,
OptionTokenTimeout,
OptionRamRoleArn,
OptionRoleSessionName,
OptionExternalId,
OptionReadTimeout,
OptionConnectTimeout,
OptionSTSRegion,
OptionSkipVerifyCert,
OptionUserAgent,
OptionSignVersion,
OptionRegion,
OptionCloudBoxID,
OptionForcePathStyle,
},
},
}
// function for FormatHelper interface
func (afc *AppendFileCommand) formatHelpForWhole() string {
return afc.command.formatHelpForWhole()
}
func (afc *AppendFileCommand) formatIndependHelp() string {
return afc.command.formatIndependHelp()
}
// Init simulate inheritance, and polymorphism
func (afc *AppendFileCommand) Init(args []string, options OptionMapType) error {
return afc.command.Init(args, options, afc)
}
// RunCommand simulate inheritance, and polymorphism
func (afc *AppendFileCommand) RunCommand() error {
afc.afOption.encodingType, _ = GetString(OptionEncodingType, afc.command.options)
afc.afOption.ossMeta, _ = GetString(OptionMeta, afc.command.options)
srcBucketUrL, err := GetCloudUrl(afc.command.args[1], afc.afOption.encodingType)
if err != nil {
return err
}
if srcBucketUrL.object == "" {
return fmt.Errorf("object key is empty")
}
payer, _ := GetString(OptionRequestPayer, afc.command.options)
if payer != "" {
if payer != strings.ToLower(string(oss.Requester)) {
return fmt.Errorf("invalid request payer: %s, please check", payer)
}
afc.commonOptions = append(afc.commonOptions, oss.RequestPayer(oss.PayerType(payer)))
}
afc.afOption.bucketName = srcBucketUrL.bucket
afc.afOption.objectName = srcBucketUrL.object
// check input file
fileName := afc.command.args[0]
stat, err := os.Stat(fileName)
if err != nil {
return err
}
if stat.IsDir() {
return fmt.Errorf("%s is dir", fileName)
}
if stat.Size() > MaxAppendObjectSize {
return fmt.Errorf("locafile:%s is bigger than %d, it is not supported by append", fileName, MaxAppendObjectSize)
}
afc.afOption.fileName = fileName
afc.afOption.fileSize = stat.Size()
// check object exist or not
client, err := afc.command.ossClient(afc.afOption.bucketName)
if err != nil {
return err
}
bucket, err := client.Bucket(afc.afOption.bucketName)
if err != nil {
return err
}
isExist, err := bucket.IsObjectExist(afc.afOption.objectName, afc.commonOptions...)
if err != nil {
return err
}
if isExist && afc.afOption.ossMeta != "" {
return fmt.Errorf("setting meta on existing append object is not supported")
}
position := int64(0)
if isExist {
//get object size
props, err := bucket.GetObjectMeta(afc.afOption.objectName, afc.commonOptions...)
if err != nil {
return err
}
position, err = strconv.ParseInt(props.Get("Content-Length"), 10, 64)
if err != nil {
return err
}
}
err = afc.AppendFromFile(bucket, position)
return err
}
func (afc *AppendFileCommand) AppendFromFile(bucket *oss.Bucket, position int64) error {
file, err := os.OpenFile(afc.afOption.fileName, os.O_RDONLY, 0660)
if err != nil {
return err
}
defer file.Close()
var options []oss.Option
if afc.afOption.ossMeta != "" {
metas, err := afc.command.parseHeaders(afc.afOption.ossMeta, false)
if err != nil {
return err
}
options, err = afc.command.getOSSOptions(headerOptionMap, metas)
if err != nil {
return err
}
}
var listener *AppendProgressListener = &AppendProgressListener{}
options = append(options, oss.Progress(listener))
options = append(options, afc.commonOptions...)
startT := time.Now()
newPosition, err := bucket.AppendObject(afc.afOption.objectName, file, position, options...)
endT := time.Now()
if err != nil {
return err
} else {
cost := endT.UnixNano()/1000/1000 - startT.UnixNano()/1000/1000
speed := float64(afc.afOption.fileSize) / float64(cost)
fmt.Printf("\nlocal file size is %d,the object new size is %d,average speed is %.2f(KB/s)\n\n", afc.afOption.fileSize, newPosition, speed)
return nil
}
}