關於文件上傳,我們一般使用OSS服務器。大致為兩種上傳方式:
詳情官網參考:https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1606.16076e28Skqaz3
本文主要介紹如何基於POST Policy的使用規則在服務端通過各種語言代碼完成簽名,並且設置上傳回調,然后通過表單直傳數據到OSS。
背景
采用服務端簽名后直傳方案有個問題:大多數情況下,用戶上傳數據后,應用服務器需要知道用戶上傳了哪些文件以及文件名;如果上傳了圖片,還需要知道圖片的大小等,為此OSS提供了上傳回調方案。
原理介紹
服務端簽名后直傳的原理如下:
- 用戶發送上傳Policy請求到應用服務器。
- 應用服務器返回上傳Policy和簽名給用戶。
- 用戶直接上傳數據到OSS。
本示例中,Web端向服務端請求簽名,然后直接上傳,不會對服務端產生壓力,而且安全可靠。但本示例中的服務端無法實時了解用戶上傳了多少文件,上傳了什么文件。如果想實時了解用戶上傳了什么文件,可以采用服務端簽名直傳並設置上傳回調。
以Java語言為例,講解在服務端通過Java代碼完成簽名,並且設置上傳回調,然后通過表單直傳數據到OSS。
前提條件
- 應用服務器對應的域名可通過公網訪問。
- 確保應用服務器已經安裝
Java 1.6
以上版本(執行命令java -version
進行查看)。 - 確保PC端瀏覽器支持JavaScript。
步驟1:配置應用服務器
步驟2:配置客戶端
步驟3:修改CORS
客戶端進行表單直傳到OSS時,會從瀏覽器向OSS發送帶有Origin
的請求消息。OSS對帶有Origin
頭的請求消息會進行跨域規則(CORS)的驗證。因此需要為Bucket設置跨域規則以支持Post方法。
說明 為了您的數據安全,實際使用時,來源欄建議您填寫自己需要的域名。更多配置信息請參見設置跨域訪問。
步驟4:體驗上傳回調
單擊選擇文件,選擇指定類型的文件,單擊開始上傳。
應用服務器核心代碼解析
應用服務器源碼包含了簽名直傳服務和上傳回調服務兩個功能。
- 簽名直傳服務
簽名直傳服務響應客戶端發送給應用服務器的GET消息,代碼片段如下:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String accessId = "<yourAccessKeyId>"; // 請填寫您的AccessKeyId。 String accessKey = "<yourAccessKeySecret>"; // 請填寫您的AccessKeySecret。 String endpoint = "oss-cn-hangzhou.aliyuncs.com"; // 請填寫您的 endpoint。 String bucket = "bucket-name"; // 請填寫您的 bucketname 。 String host = "https://" + bucket + "." + endpoint; // host的格式為 bucketname.endpoint // callbackUrl為 上傳回調服務器的URL,請將下面的IP和Port配置為您自己的真實信息。 String callbackUrl = "http://88.88.88.88:8888"; String dir = "user-dir-prefix/"; // 用戶上傳文件時指定的前綴。 // 創建OSSClient實例。 OSS ossClient = new OSSClientBuilder().build(endpoint, accessId, accessKey); try { long expireTime = 30; long expireEndTime = System.currentTimeMillis() + expireTime * 1000; Date expiration = new Date(expireEndTime); // PostObject請求最大可支持的文件大小為5 GB,即CONTENT_LENGTH_RANGE為5*1024*1024*1024。 PolicyConditions policyConds = new PolicyConditions(); policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000); policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir); String postPolicy = ossClient.generatePostPolicy(expiration, policyConds); byte[] binaryData = postPolicy.getBytes("utf-8"); String encodedPolicy = BinaryUtil.toBase64String(binaryData); String postSignature = ossClient.calculatePostSignature(postPolicy); Map<String, String> respMap = new LinkedHashMap<String, String>(); respMap.put("accessid", accessId); respMap.put("policy", encodedPolicy); respMap.put("signature", postSignature); respMap.put("dir", dir); respMap.put("host", host); respMap.put("expire", String.valueOf(expireEndTime / 1000)); // respMap.put("expire", formatISO8601Date(expiration)); JSONObject jasonCallback = new JSONObject(); jasonCallback.put("callbackUrl", callbackUrl); jasonCallback.put("callbackBody", "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"); jasonCallback.put("callbackBodyType", "application/x-www-form-urlencoded"); String base64CallbackBody = BinaryUtil.toBase64String(jasonCallback.toString().getBytes()); respMap.put("callback", base64CallbackBody); JSONObject ja1 = JSONObject.fromObject(respMap); // System.out.println(ja1.toString()); response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "GET, POST"); response(request, response, ja1.toString()); } catch (Exception e) { // Assert.fail(e.getMessage()); System.out.println(e.getMessage()); } finally { ossClient.shutdown(); } }
- 上傳回調服務
上傳回調服務響應OSS發送給應用服務器的POST消息,代碼片段如下:
protected boolean VerifyOSSCallbackRequest(HttpServletRequest request, String ossCallbackBody) throws NumberFormatException, IOException { boolean ret = false; String autorizationInput = new String(request.getHeader("Authorization")); String pubKeyInput = request.getHeader("x-oss-pub-key-url"); byte[] authorization = BinaryUtil.fromBase64String(autorizationInput); byte[] pubKey = BinaryUtil.fromBase64String(pubKeyInput); String pubKeyAddr = new String(pubKey); if (!pubKeyAddr.startsWith("https://gosspublic.alicdn.com/") && !pubKeyAddr.startsWith("https://gosspublic.alicdn.com/")) { System.out.println("pub key addr must be oss addrss"); return false; } String retString = executeGet(pubKeyAddr); retString = retString.replace("-----BEGIN PUBLIC KEY-----", ""); retString = retString.replace("-----END PUBLIC KEY-----", ""); String queryString = request.getQueryString(); String uri = request.getRequestURI(); String decodeUri = java.net.URLDecoder.decode(uri, "UTF-8"); String authStr = decodeUri; if (queryString != null && !queryString.equals("")) { authStr += "?" + queryString; } authStr += "\n" + ossCallbackBody; ret = doCheck(authStr, authorization, retString); return ret; } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String ossCallbackBody = GetPostBody(request.getInputStream(), Integer.parseInt(request.getHeader("content-length"))); boolean ret = VerifyOSSCallbackRequest(request, ossCallbackBody); System.out.println("verify result : " + ret); // System.out.println("OSS Callback Body:" + ossCallbackBody); if (ret) { response(request, response, "{\"Status\":\"OK\"}", HttpServletResponse.SC_OK); } else { response(request, response, "{\"Status\":\"verify not ok\"}", HttpServletResponse.SC_BAD_REQUEST); } }