在 Linux 上安裝並使用 AWS CLI 將檔案上傳至 S3

在 Linux 下,使用 以下指令下載 awscliv2.zip

1
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"

解壓縮並安裝

1
2
unzip awscliv2.zip
sudo ./aws/install

使用 aws configure --profile 設定profile的 Access Key IDSecrect Access Key 等資訊。

  • aws configure --profile <你想設定的profile名稱>
    1
    2
    3
    4
    5
    root@host ~# aws configure --profile my-dev
    AWS Access Key ID [None]: ASIAIOSFODNN7EXAMPLE
    AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    Default region name [None]: us-east-1
    Default output format [None]: json

設定完之後,便可以使用 AWS CLI 進行檔案上傳、下載或刪除等操作。
假設 Bucket 的名稱是 dev

使用 aws s3 ls 可以將 Bucket 中的檔案列出來

  • aws s3 ls s3://<你的Bucket名稱> --profile <你設定的profile>
    1
    2
    root@host ~# aws s3 ls s3://dev --profile my-dev
    2024-06-24 11:23:11 4 test.csv

使用 aws s3 cp 可以將檔案上傳到 Bucket 中或是從 Bucket 中下載檔案:

  • 上傳檔案至S3:使用 aws s3 cp <你要上傳檔案> s3://<你的Bucket名稱>/<要上傳的prefix與名稱> --profile <你設定的profile>

    1
    2
    root@host ~# aws s3 cp /tmp/test.csv s3://dev/test.csv --profile my-dev
    upload: /tmp/test.csv to s3://dev/test.csv
  • S3下載檔案:使用 aws s3 cp s3://<你的Bucket名稱>/<要下載的prefix與名稱> <你要下載的位置> --profile <你設定的profile>

    1
    2
    root@host ~# aws s3 cp s3://dev/test.csv /tmp/downloadfroms3.csv --profile my-dev
    download: s3://dev/test.csv to ../tmp/downloadfroms3.csv

使用 aws s3 rm 刪除在 S3 上面的檔案

  • aws s3 rm s3://<你的Bucket名稱>/<要刪除的prefix與名稱> --profile <你設定的profile>
    1
    2
    root@host ~# aws s3 rm s3://dev/test.csv  --profile my-dev
    delete: s3://dev/test.csv

Reference:

在前端直接將檔案上傳到AWS S3

在自己架設的伺服器中,如果要把檔案上傳到AWS S3,一種常見的做法是先將檔案上傳到自身伺服器然後再上傳到AWS S3,但是這樣太耗費資源了。
幸好AWS S3有方式可以讓你在前端直接將檔案上傳到S3中,以下說明如何設定前端的HTML,讓檔案直接上傳。

首先你要有可以存取BucketAWSAccessKeyIdAWSSecretAccessKey

以下資料皆來自https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-post-example.html

AWSAccessKeyId AKIAIOSFODNN7EXAMPLE
AWSSecretAccessKey wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Bucket sigv4examplebucket

HTML FORM

以下是HTML Form的例子

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
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>

<!-- 將form的 action ,設為 action="https://<你的Bucket名稱>.s3.amazonaws.com/" , 如下-->
<form action="http://sigv4examplebucket.s3.amazonaws.com/" method="post" enctype="multipart/form-data">
Key to upload:
<!-- 這個 key 將設定 S3 Object Key, 例如設為: user/user1/myfile.csv,之後在AWS S3上面看到的就是 user/user1/myfile.csv -->
<input type="input" name="key" value="user/user1/myfile.csv" /><br />
<!-- 這個 acl 將設定 S3 ACL, 如設為:public-read 表示可以被公開讀取,也可以設為 private ,表示是私有的-->
<input type="hidden" name="acl" value="public-read" />
<!-- 這個 success_action_redirect 將設定 上傳成功之後,要導向到哪個網頁,如設為:http://localhost:8080/uploaded.html -->
<input type="hidden" name="success_action_redirect" value="http://localhost:8080/uploaded.html" />
Content-Type:
<!-- 這個 Content-Type 將限定要上傳的檔案類型,可以不設定 -->
<!-- <input type="input" name="Content-Type" value="image/jpeg" /><br /> -->
<!-- 這個 x-amz-meta-uuid 應由後端產生一組隨機的字串,讓AWS S3做驗證 -->
<input type="hidden" name="x-amz-meta-uuid" value="14365123651274" />
<!-- 這個 x-amz-server-side-encryption 說明使用哪種加密方式,例如 AES256 -->
<input type="hidden" name="x-amz-server-side-encryption" value="AES256" />

<!-- 這個 X-Amz-Credential 由 <AWSAccessKeyId>/<日期>/<Bucket-Region>/<RegionService>/<Signing> 組成,RegionService和Signing是固定的就是s3和aws4_request-->
<input type="text" name="X-Amz-Credential" value="AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request" />
<!-- 這個 X-Amz-Algorithm說明建立這個Request的簽名使用哪種演算法,例如 AWS4-HMAC-SHA256 -->
<input type="text" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" />
<!-- 這個 X-Amz-Date 說明建立這個Request的簽名的日期,為可選的,需要為 ISO 8601 basic format (YYYYMMDD'T'HHMMSS'Z') 格式 -->
<input type="text" name="X-Amz-Date" value="20151229T000000Z" />

Tags for File:
<!-- 這個 x-amz-meta-tag 是可選的 -->
<!-- <input type="input" name="x-amz-meta-tag" value="" /><br /> -->
<!-- 這個 Policy 為Base64編碼的字串,在下面說明 -->
<input type="hidden" name="Policy" value='<Base64-encoded policy string>' />
<!-- 這個 X-Amz-Signature 是簽名,包含AWSSecretAccessKey的值,應由後端產生,在下面說明 -->
<input type="hidden" name="X-Amz-Signature" value="<Signature-value>" />
File:
<input type="file" name="file" /> <br />
<input type="submit" name="submit" value="Upload to Amazon S3" />
</form>

</html>

Base64-encoded policy string

Policy 應由後端產生,他是一個JSON物件,以下是一個例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{ "expiration": "2015-12-30T12:00:00.000Z",
"conditions": [
{"bucket": "sigv4examplebucket"},
["starts-with", "$key", "user/user1/"],
{"acl": "public-read"},
{"success_action_redirect": "http://localhost:8080/uploaded.html"},
// ["starts-with", "$Content-Type", "image/"],
{"x-amz-meta-uuid": "14365123651274"},
{"x-amz-server-side-encryption": "AES256"},
// ["starts-with", "$x-amz-meta-tag", ""],
{"x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"},
{"x-amz-algorithm": "AWS4-HMAC-SHA256"},
{"x-amz-date": "20151229T000000Z" }
]
}
expiration 表示這個上傳的Request可用的期限
conditions 設定條件,在檔案上傳前AWS S3會檢查一下,是否和HTML Form中的相符合
bucket Bucket 名稱,此範例為 sigv4examplebucket
“starts-with”, “$key” 要放在這個Bucket的 Prefix,此範例為user/user1/ ,在HTML Form中的 key 需要符合在此設定的前綴祠才可以,如 user/user1/myfile.csv
acl 設定 S3 ACL, 如設為:public-read 表示可以被公開讀取,也可以設為 private ,表示是私有的
success_action_redirect 上傳成功之後,要導向到哪個網頁,如設為:http://localhost:8080/uploaded.html
x-amz-meta-uuid 後端產生的UUID
x-amz-server-side-encryption 說明使用哪種加密方式, AES256
“starts-with”, “$Content-Type” 上傳的 content type,可以不設定
x-amz-credential /<日期>/// 組成,RegionService和Signing是固定的就是s3和aws4_request
x-amz-algorithm 說明建立這個Request的簽名使用哪種演算法,例如 AWS4-HMAC-SHA256
x-amz-date 建立這個Request的簽名的日期,為可選的,需要為 ISO 8601 basic format (YYYYMMDD’T’HHMMSS’Z’) 格式

注意,如果你在HTML Form中沒有設定某些參數,那麼在Policy中也不要設定,例如我在HTML Form中沒有設定 ["starts-with", "$Content-Type", "image/"]["starts-with", "$x-amz-meta-tag", ""],那麼在 Policy中也不要設定。

之後由後端使用Base64將這段字串編碼,這個Base64字串編碼就是要放在 HTML Form 中的 Policy,以下是Java產生的範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
JSONObject j = new JSONObject();

j.put("expiration", "2015-12-30T12:00:00.000Z");
j.put("conditions",
new JSONArray()
.put(new JSONObject().put("bucket", "sigv4examplebucket"))
.put(new JSONArray().put("starts-with").put("$key").put("user/user1/"))
.put(new JSONObject().put("acl", "private"))
.put(new JSONObject().put("success_action_redirect", "http://localhost:8080"))
.put(new JSONObject().put("x-amz-meta-uuid", "14365123651274"))
.put(new JSONObject().put("x-amz-server-side-encryption", "AES256"))
.put(new JSONObject().put("x-amz-credential", "AKIAIOSFODNN7EXAMPLE/20151229/us-east-1/s3/aws4_request"))
.put(new JSONObject().put("x-amz-algorithm", "AWS4-HMAC-SHA256"))
.put(new JSONObject().put("x-amz-date", "20151229T000000Z"))
);
// 這個 Policy 就是要放在 HTML Form 中的 Policy
String policy = Base64.getEncoder().encodeToString(j.toString().getBytes());

Signature-value

最後產生 Signature ,Java 可以直接使用有人提供的產生方式,來自 https://gist.github.com/phstudy/3523576726d74a0410f8

1
2
3
4
5
6
7
8
9
10
// 在 AccessSecretKey前面要加上 AWS4
String accessSecretKey = "AWS4" + "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
String date = "20151229";
String region = "us-east-1";
String regionService = "s3";
String signing = "aws4_request";
String stringToSign = policy;

// 這個 signature 字串,就是要放在 HTML Form 中的 X-Amz-Signature
String signature = getSignatureV4(accessSecretKey, date, region, regionService, signing, stringToSign);

以上動作完成之後,還需要為你的Bucket設定 Cross-origin resource sharing (CORS)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"POST",
"GET",
"PUT"
],
"AllowedOrigins": [
"http://www.mywebsite.com/"
],
"ExposeHeaders": []
}
]

Bucket沒有設定Cross-origin resource sharing (CORS)會出現 CORS policy 錯誤

1
Access to XMLHttpRequest at 'https://<你的bucket>.s3.amazonaws.com/' from origin 'https://你的網站.com ' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

如果是還在測試開發的話,可以先將Chrome的web-security關閉,Mac使用以下指令,其他設定可以參考: disable-same-origin-policy-in-chrome

1
open -n -a "Google Chrome" --args --user-data-dir=/tmp/temp_chrome_user_data_dir http://localhost:8080/ --disable-web-security

Reference: