Amazon S3 PreSigned Url
Amazon S3 PreSigned Url
簡介 |
可預先產生已簽名並限定時間的URL供使用者上傳或是下載檔案,使得真正的AWS S3位置可以確保隱藏並加以控管。 |
作者 |
詹國忠 |
1.前言
- 為了節約成本,可將靜態資源存儲在AWS S3上。但S3資源默認是私有的,只有Owner登錄後才能訪問,如果將資源改為Public的,這又不安全,那怎麼辦呢?可以使用AWS SDK生成預簽名的URL,限定用戶在有限的時間內可訪問。
- 開發工具使用Eclipse。
2.目的
- 可預先產生已簽名並限定時間的URL供使用者上傳或是下載檔案,使得真正的AWS S3位置可以確保隱藏並加以控管。
3.開始前準備
- 本架構建立於以下版本的環境:
- JDK6以上
- Eclipse Mars.2 Release (4.5.2)
- Maven
- AWS SDK for JAVA
4.下傳檔案
4.1使用以下程式碼產生URL
import java.io.IOException;
import java.net.URL;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
public class GeneratePreSignedUrl {
private static String bucketName = "*** Provide a bucket name ***";
private static String objectKey = "*** Provide an object key ***";
public static void main(String[] args) throws IOException {
AmazonS3 s3client = new AmazonS3Client(new ProfileCredentialsProvider());
try {
System.out.println("Generating pre-signed URL.");
java.util.Date expiration = new java.util.Date();
long milliSeconds = expiration.getTime();
milliSeconds += 1000 * 60 * 60; // Add 1 hour.
expiration.setTime(milliSeconds);
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(bucketName, objectKey);
generatePresignedUrlRequest.setMethod(HttpMethod.GET);
generatePresignedUrlRequest.setExpiration(expiration);
URL url = s3client.generatePresignedUrl(generatePresignedUrlRequest);
System.out.println("Pre-Signed URL = " + url.toString());
} catch (AmazonServiceException exception) {
System.out.println("Caught an AmazonServiceException, " +
"which means your request made it " +
"to Amazon S3, but was rejected with an error response " +
"for some reason.");
System.out.println("Error Message: " + exception.getMessage());
System.out.println("HTTP Code: " + exception.getStatusCode());
System.out.println("AWS Error Code:" + exception.getErrorCode());
System.out.println("Error Type: " + exception.getErrorType());
System.out.println("Request ID: " + exception.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException, " +
"which means the client encountered " +
"an internal error while trying to communicate" +
" with S3, " +
"such as not being able to access the network.");
System.out.println("Error Message: " + ace.getMessage());
}
}
}
4.2程式碼內容
- 以上述程式碼可產生出已簽名的URL,使私有的S3位置檔案提供下載
- 紅色字體部分為設定此URL的有效時間,以此程式碼為例,產出的URL在一個小時後就會逾期無法再使用
- 藍色字體部分是自己私有的S3的bucket name及Object key
- 產生的URL範例: https:// example-bucket.s3.amazonaws.com/sample.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20170630T080349Z&X-Amz-SignedHeaders=host&X-Amz-Expires=3600&X-Amz-Credential=AKIAJUC5OYTA4E5EPWRA%2F20170630%2Fap-southeast-1%2Fs3%2Faws4_request&X-Amz-Signature=69ef7fe08eb90e4f6c73bbada7379970a63fff53fa0502a1b364d67e58e273a4
5.上傳檔案
5.1使用以下程式碼產生URL
import java.io.IOException;
import java.net.URL;
import com.amazonaws.AmazonClientException;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.HttpMethod;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest;
public class GeneratePreSignedUrl {
private static String bucketName = "*** Provide a bucket name ***";
private static String objectKey = "*** Provide an object key ***";
public static void main(String[] args) throws IOException {
AmazonS3 s3client = new AmazonS3Client(new ProfileCredentialsProvider());
try {
System.out.println("Generating pre-signed URL.");
java.util.Date expiration = new java.util.Date();
long milliSeconds = expiration.getTime();
milliSeconds += 1000 * 60 * 60; // Add 1 hour.
expiration.setTime(milliSeconds);
GeneratePresignedUrlRequest generatePresignedUrlRequest =
new GeneratePresignedUrlRequest(bucketName, objectKey);
generatePresignedUrlRequest.setMethod(HttpMethod.GET);
generatePresignedUrlRequest.setExpiration(expiration);
URL url = s3client.generatePresignedUrl(generatePresignedUrlRequest);
System.out.println("Pre-Signed URL = " + url.toString());
} catch (AmazonServiceException exception) {
System.out.println("Caught an AmazonServiceException, " +
"which means your request made it " +
"to Amazon S3, but was rejected with an error response " +
"for some reason.");
System.out.println("Error Message: " + exception.getMessage());
System.out.println("HTTP Code: " + exception.getStatusCode());
System.out.println("AWS Error Code:" + exception.getErrorCode());
System.out.println("Error Type: " + exception.getErrorType());
System.out.println("Request ID: " + exception.getRequestId());
} catch (AmazonClientException ace) {
System.out.println("Caught an AmazonClientException, " +
"which means the client encountered " +
"an internal error while trying to communicate" +
" with S3, " +
"such as not being able to access the network.");
System.out.println("Error Message: " + ace.getMessage());
}
}
}
可以看到產生URL的方式與下載檔案的時候一樣,差別在於之後的上傳檔案的使用方式
5.2使用java方式上傳
l 程式碼
public static void UploadObject(URL url, String content) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
OutputStreamWriter out = new
OutputStreamWriter(connection.getOutputStream());
out.write(content);
out.close();
int responseCode = connection.getResponseCode();
System.out.println("Service returned response code " + responseCode);
}
l 由以上程式碼可以看到,只需直接使用產生好的URL即可上傳,不會暴露出S3檔案的bucket name和Object key
5.3使用javascript方式上傳
l 程式碼
<script type="text/javascript">
var s3presignedUrl = '<%=preSignedUrl%>';
s3presignedUrl = decodeURIComponent(s3presignedUrl);
var fileChooser = document.getElementById('file-chooser');
var button = document.getElementById('upload-button');
button.addEventListener('click', function() {
var file = fileChooser.files[0];
$.ajax({
url : s3presignedUrl,
type : "PUT",
data : file,
dataType : "text",
cache : false,
contentType : file.type,
processData : false,
xhr: function() {
var myXhr = $.ajaxSettings.xhr();
if(myXhr.upload){
myXhr.upload.addEventListener('progress',progress, false);
}
return myXhr;
}
})
.done(function(){
console.log("upload completed...." + new Date());
alert("done");
console.info('YEAH', s3presignedUrl.split('?')[0].substr(6));
})
.fail(function(){
alert("fail");
console.error('damn...');
});
}, false);
function progress(e){
if(e.lengthComputable){
var max = e.total;
var current = e.loaded;
var Percentage = (current * 100)/max;
console.log(Percentage);
if(Percentage >= 100) {
// process completed
}
}
}
</script>
l 以上的程式碼範例即可使用由後端產生的已簽名的URL在前端頁面使用ajax的方式上傳檔案
l 紅色字體的部分為此ajax function的callback method,由此callback可得知目前檔案上傳的進度並以百分比顯示,若另外再搭配jquery的進度條plugin,就可以在前端顯示各式風格的上傳進度條