Skip to content

Commit 16907c6

Browse files
committed
add AWS fetch and list command
1 parent a467b0b commit 16907c6

File tree

3 files changed

+338
-52
lines changed

3 files changed

+338
-52
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# 2.3.8
22
1. 添加batchdelete 自定义分隔符
33
2. batchfetch支持自定义上传Host设置
4+
3. 添加awsfetch抓取亚马逊空间数据到七牛空间
5+
4. 添加awslist列举亚马逊空间文件
46

57
# 2.3.7
68
1. 加入forbidden命令,可以禁用或者解禁文件

cmd/awsfetch.go

Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/aws/aws-sdk-go/aws"
9+
"github.com/aws/aws-sdk-go/aws/awserr"
10+
"github.com/aws/aws-sdk-go/aws/credentials"
11+
"github.com/aws/aws-sdk-go/aws/session"
12+
"github.com/aws/aws-sdk-go/service/s3"
13+
"github.com/qiniu/qshell/iqshell"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
type awsfetchOptions struct {
18+
fetchConfig
19+
awslistOptions
20+
}
21+
22+
type awslistOptions struct {
23+
// aws continuation token
24+
ctoken string
25+
26+
delimiter string
27+
28+
maxKeys int64
29+
30+
prefix string
31+
32+
// aws id and secretKey
33+
id string
34+
secretKey string
35+
}
36+
37+
func (lo *awslistOptions) Run(cmd *cobra.Command, positionalArgs []string) {
38+
39+
lo.checkOptions()
40+
41+
awsBucket := positionalArgs[0]
42+
region := positionalArgs[1]
43+
// check AWS region
44+
if region == "" {
45+
fmt.Fprintf(os.Stderr, "AWS region cannot be empty\n")
46+
os.Exit(1)
47+
}
48+
49+
// AWS related code
50+
s3session := session.New()
51+
s3session.Config.WithRegion(region)
52+
s3session.Config.WithCredentials(credentials.NewStaticCredentials(lo.id, lo.secretKey, ""))
53+
54+
svc := s3.New(s3session)
55+
input := &s3.ListObjectsV2Input{
56+
Bucket: aws.String(awsBucket),
57+
Prefix: aws.String(lo.prefix),
58+
Delimiter: aws.String(lo.delimiter),
59+
MaxKeys: aws.Int64(lo.maxKeys),
60+
}
61+
if lo.ctoken != "" {
62+
input.ContinuationToken = aws.String(lo.ctoken)
63+
}
64+
65+
for {
66+
result, err := svc.ListObjectsV2(input)
67+
68+
if err != nil {
69+
if aerr, ok := err.(awserr.Error); ok {
70+
switch aerr.Code() {
71+
case s3.ErrCodeNoSuchBucket:
72+
fmt.Fprintln(os.Stderr, s3.ErrCodeNoSuchBucket, aerr.Error())
73+
default:
74+
fmt.Fprintln(os.Stderr, aerr.Error())
75+
}
76+
} else {
77+
// Print the error, cast err to awserr.Error to get the Code and
78+
// Message from an error.
79+
fmt.Fprintln(os.Stderr, err.Error())
80+
}
81+
os.Exit(1)
82+
}
83+
for _, obj := range result.Contents {
84+
if strings.HasSuffix(*obj.Key, "/") && *obj.Size == 0 { // 跳过目录
85+
continue
86+
}
87+
fmt.Printf("%s\t%d\t%s\t%s\n", *obj.Key, *obj.Size, *obj.ETag, *obj.LastModified)
88+
}
89+
90+
if *result.IsTruncated {
91+
input.ContinuationToken = result.NextContinuationToken
92+
} else {
93+
break
94+
}
95+
}
96+
97+
}
98+
99+
func (lo *awslistOptions) checkOptions() {
100+
if lo.id == "" || lo.secretKey == "" {
101+
fmt.Fprintf(os.Stderr, "AWS ID and SecretKey cannot be empty\n")
102+
os.Exit(1)
103+
}
104+
if lo.maxKeys <= 0 || lo.maxKeys > 1000 {
105+
lo.maxKeys = 1000
106+
}
107+
}
108+
109+
func awsUrl(awsBucket, region, key string) string {
110+
return fmt.Sprintf("https://%s.s3-%s.amazonaws.com/%s", awsBucket, region, key)
111+
}
112+
113+
func (o *awsfetchOptions) Run(cmd *cobra.Command, positionalArgs []string) {
114+
o.checkOptions()
115+
116+
if o.threadCount <= 0 || o.threadCount >= 1000 {
117+
o.threadCount = 20
118+
}
119+
120+
o.initBucketManager()
121+
o.initFileExporter()
122+
o.initUpHost(positionalArgs[2])
123+
124+
// check AWS region
125+
if positionalArgs[1] == "" {
126+
fmt.Fprintf(os.Stderr, "AWS region cannot be empty\n")
127+
os.Exit(1)
128+
}
129+
130+
awsBucket, region, qiniuBucket := positionalArgs[0], positionalArgs[1], positionalArgs[2]
131+
132+
// AWS related code
133+
s3session := session.New()
134+
s3session.Config.WithRegion(region)
135+
s3session.Config.WithCredentials(credentials.NewStaticCredentials(o.id, o.secretKey, ""))
136+
137+
svc := s3.New(s3session)
138+
input := &s3.ListObjectsV2Input{
139+
Bucket: aws.String(awsBucket),
140+
Prefix: aws.String(o.prefix),
141+
Delimiter: aws.String(o.delimiter),
142+
MaxKeys: aws.Int64(o.maxKeys),
143+
}
144+
if o.ctoken != "" {
145+
input.ContinuationToken = aws.String(o.ctoken)
146+
}
147+
itemc := make(chan *iqshell.FetchItem)
148+
donec := make(chan struct{})
149+
150+
go fetchChannel(itemc, donec, &o.fetchConfig)
151+
152+
for {
153+
result, err := svc.ListObjectsV2(input)
154+
155+
if err != nil {
156+
if aerr, ok := err.(awserr.Error); ok {
157+
switch aerr.Code() {
158+
case s3.ErrCodeNoSuchBucket:
159+
fmt.Fprintln(os.Stderr, s3.ErrCodeNoSuchBucket, aerr.Error())
160+
default:
161+
fmt.Fprintln(os.Stderr, aerr.Error())
162+
}
163+
} else {
164+
// Print the error, cast err to awserr.Error to get the Code and
165+
// Message from an error.
166+
fmt.Fprintln(os.Stderr, err.Error())
167+
}
168+
close(itemc)
169+
os.Exit(1)
170+
}
171+
for _, obj := range result.Contents {
172+
if strings.HasSuffix(*obj.Key, "/") && *obj.Size == 0 { // 跳过目录
173+
continue
174+
}
175+
176+
item := &iqshell.FetchItem{
177+
Bucket: qiniuBucket,
178+
Key: *obj.Key,
179+
RemoteUrl: awsUrl(awsBucket, region, *obj.Key),
180+
}
181+
itemc <- item
182+
}
183+
184+
if *result.IsTruncated {
185+
input.ContinuationToken = result.NextContinuationToken
186+
} else {
187+
break
188+
}
189+
}
190+
close(itemc)
191+
192+
<-donec
193+
}
194+
195+
// NewCmdAwsFetch 返回一个cobra.Command指针
196+
func NewCmdAwsFetch() *cobra.Command {
197+
options := awsfetchOptions{}
198+
199+
awsFetch := &cobra.Command{
200+
Use: "awsfetch [-p <Prefix>] [-n <maxKeys>] [-m <ContinuationToken>] [-c <threadCount>][-u <Qiniu UpHost>] -S <AwsSecretKey> -A <AwsID> <awsBucket> <awsRegion> <qiniuBucket>",
201+
Short: "Copy data from AWS bucket to qiniu bucket",
202+
Args: cobra.ExactArgs(3),
203+
Run: options.Run,
204+
}
205+
206+
awsFetch.Flags().StringVarP(&options.prefix, "prefix", "p", "", "list AWS bucket with this prefix if set")
207+
awsFetch.Flags().Int64VarP(&options.maxKeys, "max-keys", "n", 1000, "list AWS bucket with numbers of keys returned each time limited by this number if set")
208+
awsFetch.Flags().StringVarP(&options.ctoken, "continuation-token", "m", "", "AWS list continuation token")
209+
awsFetch.Flags().IntVarP(&options.threadCount, "thead-count", "c", 20, "maximum of fetch thread")
210+
awsFetch.Flags().StringVarP(&options.upHost, "up-host", "u", "", "Qiniu fetch up host")
211+
awsFetch.Flags().StringVarP(&options.secretKey, "aws-secret-key", "S", "", "AWS secret key")
212+
awsFetch.Flags().StringVarP(&options.id, "aws-id", "A", "", "AWS ID")
213+
awsFetch.Flags().StringVarP(&options.successFname, "success-list", "s", "", "success fetch key list")
214+
awsFetch.Flags().StringVarP(&options.failureFname, "failure-list", "e", "", "error fetch key list")
215+
216+
return awsFetch
217+
}
218+
219+
// NewCmdAwsList 返回一个cobra.Command指针
220+
// 该命令列举亚马逊存储空间中的文件, 会忽略目录
221+
func NewCmdAwsList() *cobra.Command {
222+
options := awslistOptions{}
223+
224+
awsList := &cobra.Command{
225+
Use: "awslist [-p <Prefix>] [-n <maxKeys>] [-m <ContinuationToken>] -S <AwsSecretKey> -A <AwsID> <awsBucket> <awsRegion>",
226+
Short: "List Objects in AWS bucket",
227+
Args: cobra.ExactArgs(2),
228+
Run: options.Run,
229+
}
230+
231+
awsList.Flags().StringVarP(&options.prefix, "prefix", "p", "", "list AWS bucket with this prefix if set")
232+
awsList.Flags().Int64VarP(&options.maxKeys, "max-keys", "n", 1000, "list AWS bucket with numbers of keys returned each time limited by this number if set")
233+
awsList.Flags().StringVarP(&options.ctoken, "continuation-token", "m", "", "AWS list continuation token")
234+
awsList.Flags().StringVarP(&options.secretKey, "aws-secret-key", "S", "", "AWS secret key")
235+
awsList.Flags().StringVarP(&options.id, "aws-id", "A", "", "AWS ID")
236+
237+
return awsList
238+
}
239+
240+
func init() {
241+
RootCmd.AddCommand(NewCmdAwsFetch())
242+
RootCmd.AddCommand(NewCmdAwsList())
243+
}

0 commit comments

Comments
 (0)