S3使用笔记-Node版

最近有一个项目,需要存储设计师的素材,比如psd、sketch、图片等等。得知公司有搭建统一的S3服务,我就准备直接用一下这个服务,这样省去自己存储文件的环节,也不用考虑备份等事情了。

目前公司用Node的人很少,至于S3则尚未有人用Node操作过,因此我是公司第一个吃螃蟹的人。

遇到的问题

内网获取S3连接地址 内网只能用IP直连

2020.11.17:内网不能通过中台服务的方式访问S3:

内网无法访问s3.base,返回403,猜测是因为走中台服务,经过了Arsenal-Auth验证这一层;而S3的SDK没有设置header的api,就导致中台权限验证不通过。

所以内网只能采用IP直连的方式连接S3.

测试环境和正式环境是无需采用这种方式的,只需要配置好host即可。

另:申请S3服务,只需要申请正式环境的就可以了,测试环境和内网开发环境,是所有应用公用token的,无需重复申请。

由于公司的S3没有接入中台系统,因此必须通过服务提供方的IP端口进行访问。但是服务提供方和我们不在同一个网段,无法连通。经过咨询,了解到可以通过绑定服务依赖+服务发现的方式,在内网获取一个我们网段可用的地址。

流程如下:

1、在Arsenal中台的应用中,设置我们的应用依赖于S3服务。

2、在内网注册我们的应用:

1
curl -H "Host:pilot.myhexin.com" 'http://10.0.x.249:8080/register/reg_app?app_name=iwc-datav-topology-app-pod'

这一步需要在中台添加应用后,过一阵再执行,否则可能无法成功获取中台的应用信息,感觉是数据同步有一定的延时。

3、发现我们的应用所依赖的服务:

1
curl -H "Host:pilot.myhexin.com" 'http://10.0.x.249:8080/register/find_service?app_name=iwc-datav-topology-app-pod'

这一步同样需要在第二步完成后过一阵再执行,否则可能无法成功获取IP端口信息。

执行完第三步后,如果正常的话,就会返回IP端口信息了,大致是如下格式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"data": {
"passport.base": {
"protocol": ["http://10.0.x.249:80"],
"qps": "0",
"version": "",
"config": {}
},
"s3.base": {
"protocol": ["tcp://10.0.x.249:23006"],
"qps": "0",
"version": "",
"config": {}
}
},
"status_code": 0,
"status_msg": "success"
}

使用其中s3.base的IP端口,即可连接S3服务了。

这里额外记录一个kafka的,以备后续使用:

1
curl -H "Host:pilot.myhexin.com" 'http://10.0.x.249:8080/register/search_inst?obj_name=s3.base'

关于配置endpoint的坑

S3默认的Path Style是bucket.domain.com,比如我们设置了endpoint为s3-server,操作某个bucket(比如testbucket)的时候,默认发出去的请求地址就会变成testbucket.s3-server。

我用的不是Amazon的S3服务,是公司自己搭建的S3,因此必须要修改访问S3的地址。我参考PHP的示例代码捣鼓了半天,结果一直不行。最终发现需要使用s3ForcePathStyle这个配置(Java中是通过withPathStyleAccessEnabled(true)方法来设置的),且需要将地址初始化为AWS.Endpoint实例,而不是直接传入配置。

代码大致如下:

1
2
3
4
5
6
7
8
9
10
11
let options = {
apiVersion: '2006-03-01',
region: 'HZ',
endpoint: new AWS.Endpoint("http://10.0.x.x:12345"),
s3ForcePathStyle: true,
credentials: {
accessKeyId: 'your accessKeyId',
secretAccessKey: 'your secretAccessKey'
}
};
new AWS.S3(options);

常用操作代码

我把几个常用的操作测试了一遍,记录如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Load the SDK and UUID
var AWS = require('aws-sdk');
var uuid = require('uuid');
var fs = require('fs');

class S3 {

constructor(options) {
//TODO:参数校验

/**
* S3连接对象的实例
*/
this._instance = new AWS.S3(options);
}


listBuckets() {
this._instance.listBuckets(function (err, data) {
if (err) {
console.log("err:", err);

} else {
console.log("data:", data.Buckets);

}
});
};

/**
* @param {object} bucketParams
* {Bucket: 'datav-resource3',ACL: 'public-read'}
*/
createBucket(bucketParams) {
this._instance.createBucket(bucketParams, function (err, data) {
if (err) {
console.log("err:", err);

} else {
console.log("data:", data.Location);

}
});
}

/**
* @param {object} bucketParams
* {Bucket: 'datav-resource3'}
*/
deleteBucket(bucketParams) {
this._instance.deleteBucket(bucketParams, function (err, data) {
if (err) {
console.log("err:", err);

} else {
console.log("data:", data);

}
});
}
/**
* @param {object} uploadParams
* {Bucket: 'datav-resource3', File: './sample.js'}
*/
upload(uploadParams) {
var path = require('path');
var file = uploadParams.File;

// Configure the file stream and obtain the upload parameters
var fileStream = fs.createReadStream(file);
fileStream.on('error', function (err) {
console.log('File Error', err);
});
uploadParams.Body = fileStream;
uploadParams.Key = path.basename(file);

this._instance.upload(uploadParams, function (err, data) {
if (err) {
console.log("Error", err);
} if (data) {
console.log("Upload Success", data.Location);
}
});
}
/**
* @param {string} bucketName
*/
listObjects(bucketName) {
var bucketParams = {
Bucket: bucketName,
};
// Call S3 to obtain a list of the objects in the bucket
this._instance.listObjects(bucketParams, function (err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
}
});
}
/**
* @param {string} bucketName
* @param {string} key
*/
getObject(bucketName, key) {
var bucketParams = {
Bucket: bucketName,
Key: key
};

// Call S3 to obtain a list of the objects in the bucket
this._instance.getObject(bucketParams, function (err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
fs.writeFileSync('object.js', data.Body);
}
});
}

/**
* @param {string} bucketName
* @param {string} key
*/
deleteObject(bucketName, key) {
var bucketParams = {
Bucket: bucketName,
Key: key
};

// Call S3 to obtain a list of the objects in the bucket
this._instance.deleteObject(bucketParams, function (err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
fs.writeFileSync('object.js', data.Body);
}
});
}

getObjectPath(bucketName, key) {

}

}

let options = {
apiVersion: '2006-03-01',
region: 'HZ',
endpoint: new AWS.Endpoint("http://10.0.x.x:12345"),
s3ForcePathStyle: true,
credentials: {
accessKeyId: 'your accessKeyId',
secretAccessKey: 'your secretAccessKey'
}
};
let s3 = new S3(options);
s3.listBuckets();

参考文档

S3概念介绍:

https://docs.aws.amazon.com/AmazonS3/latest/dev/Introduction.html

NodeJS的API:

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/getting-started-nodejs.html

aws-sdk:

https://github.com/aws/aws-sdk-js

https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/

NodeJS操作S3的示例代码:

https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javascript/example_code/s3

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-node-examples.html

操作Bucket的示例:

https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/s3-example-creating-buckets.html

Java版的教程,同事亲测可用:

http://public-cloud-doc.nos-eastchina1.126.net/S3-JAVA-SDK.html