简介
前文我们介绍了上传图片文件到阿里云的两种方案:
- 传统方式上传,通过web表单、小程序上传组件等,先把图片传到业务服务器,然后业务服务器再把图片转存到OSS。
- 前端先从业务服务器拿一个上传凭证,然后带着这个凭证直接把图片上传到阿里云OSS。
第一种方案我们在Springboot上传图片到阿里云OSS新手教程(完整代码)中有详细介绍,需要的朋友可以阅读参考一下。本文重点介绍第二种方案:WEB前端直传图片到阿里云OSS。
Web直传参数介绍
逻辑描述:和传统的web表单上传一样,web直传也是通过post请求上传文件。区别是web直传的post请求,请求body中除了文件外,还需要携带一些其他阿里云OSS的验证参数。一句话描述,就是传统表单的上传的POST请求,在body内再增加一些参数,就可以web直传了。
具体参数如下:
key
:文件的路径+名称信息。比如你要把1.jpg上传到阿里云OSS的data/test/666/目录,那么这个key就是data/test/666/1.jpg。policy
:上传策略。可以理解为上传的配置信息,比如文件的存放位置、该上传凭证的有效时间等等。OSSAccessKeyId
:阿里云账号的AccessKey,这个就不做介绍了,直接在阿里云后台复制过来就行。signature
:签名信息。根据一定的规则和信息生成的签名信息,保证只有你自己才可以往你的阿里云OSS上传东西。签名计算方法为Signature = base64(hmac-sha1(base64(policy), AccessKeySecret))
。后面会有代码演示签名。callback
:回调信息。就是当前端上传图片文件到阿里云OSS后,如果这里回调信息不为空,那么阿里云OSS会再发一起一个HTTP请求,请求你callback里配置的这个地址,并携带一些参数。可以理解为上传成功后,让阿里云OSS发起一个请求通知你的服务器。回调里可以携带诸如图片大小、格式、尺寸等等信息。
完整上传代码
因为前端有很多很多种,如果你是微信小程序,你可以通过HTTP请求来从自己的业务服务器获取上传凭证;如果你是VUE的网页,也可以通过AXIOS等ajax库来获取上传凭证;如果你是使用Vue+ElementUI这种方式,那么只要在el-upload
组件里,通过before-upload
提前请求业务服务器,拿到上传凭证即可。
为了照顾大家使用的不同前端技术,我这里用最传统的表单来演示,你使用的时候,根据你自己使用的前端技术,稍作改动即可。
springboot图片上传controller代码
下面代码中,主要是两个请求:
- 第一个请求是返回上传网页的,使用的thymeleaf模板引擎。
- 第二个请求是一个ajax API接口,前端客户端请求这个API,就能拿到上传阿里云OSS所需的凭证了。
代码如下:
package com.coderbbb.book1.controller;
import com.coderbbb.book1.database.dto.JsUploadDTO;
import com.coderbbb.book1.service.AliyunOssService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UploadController2 {
@Autowired
private AliyunOssService aliyunOssService;
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@RequestMapping(value = "/upload/webPage2", method = RequestMethod.GET)
public String webPage() {
return "webPage2";
}
@RequestMapping(value = "/upload/webPage2/api", method = RequestMethod.GET)
@ResponseBody
public JsUploadDTO webPost() {
try {
return aliyunOssService.jsUploadFile("data/", "ttt.jpg", 100 * 1024 * 1024, null);
} catch (Exception e) {
logger.error("upload err",e);
}
return null;
}
}
springboot直接上传阿里云OSS凭证代码
上面的controller代码中,使用了一个名为AliyunOssService
的service,这个service里封装了构造web直传所需数据的方法,代码如下:
package com.coderbbb.book1.service;
import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.auth.DefaultCredentialProvider;
import com.aliyun.oss.common.comm.ResponseMessage;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.*;
import com.coderbbb.book1.database.dto.JsUploadDTO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
/**
* @Author: longge93
* @Date: 2021/6/10 13:32
*/
@Service
public class AliyunOssService {
private final String ACCESS_KEY = "access key";
private final String SECRET_KEY = "secret key";
private final String ENDPOINT = "oss-accelerate.aliyuncs.com";
private final String BUCKET = "66tec-ap";
private static OSSClient ossClient;
private synchronized void createOssClient() {
if (ossClient == null) {
DefaultCredentialProvider defaultCredentialProvider = new DefaultCredentialProvider(ACCESS_KEY, SECRET_KEY);
ClientConfiguration config = null;
ossClient = new OSSClient(ENDPOINT, defaultCredentialProvider, config);
}
}
private OSSClient getOssClient() {
if (ossClient == null) {
createOssClient();
}
return ossClient;
}
public String uploadInputStreamFile(String key, InputStream inputStream, CannedAccessControlList acl, Callback callback) throws Exception {
PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET, key, inputStream);
ObjectMetadata metadata = new ObjectMetadata();
metadata.setObjectAcl(acl);
putObjectRequest.setMetadata(metadata);
if (callback != null) {
putObjectRequest.setCallback(callback);
}
PutObjectResult putObjectResult = getOssClient().putObject(putObjectRequest);
ResponseMessage responseMessage = putObjectResult.getResponse();
if (responseMessage != null && callback != null) {
InputStream content = responseMessage.getContent();
String result = new String(content.readAllBytes());
content.close();
return result;
}
return null;
}
/**
* 传入上传基本路径和文件名,返回JS直传所需的加密信息和签名
*
* @param basePath
* @param fileName
* @param uploadCallbackStr 回调
* @return
* @throws Exception
*/
public JsUploadDTO jsUploadFile(String basePath, String fileName, long maxSize, String uploadCallbackStr) throws Exception {
//上传文件签名信息过期时间
long UPLOAD_EXPIRE_TIME = 60 * 5 * 1000;
Date expireTime = new Date(System.currentTimeMillis() + UPLOAD_EXPIRE_TIME);
PolicyConditions policyConds = new PolicyConditions();
//文件大小
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, maxSize);
//文件路径限制
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, basePath);
String postPolicy = getOssClient().generatePostPolicy(expireTime, policyConds);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = getOssClient().calculatePostSignature(postPolicy);
JsUploadDTO jsUploadDTO = new JsUploadDTO();
jsUploadDTO.setAccessId(ACCESS_KEY);
jsUploadDTO.setExpireTime(expireTime);
jsUploadDTO.setHost(BUCKET + "." + ENDPOINT);
jsUploadDTO.setPath(basePath + fileName);
jsUploadDTO.setPolicy(encodedPolicy);
jsUploadDTO.setSignature(postSignature);
if (StringUtils.isNotEmpty(uploadCallbackStr)) {
jsUploadDTO.setCallBack(uploadCallbackStr);
}
return jsUploadDTO;
}
}
其中,uploadInputStreamFile
函数是上一篇文章中,通过业务服务器上传OSS的代码,你用不到可以删掉。
springboot所需阿里云OSS SDK
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.13.2</version>
</dependency>
HTML前端网页代码
这部分代码就是传统的表单上传,为了更像Vue、微信小程序等上传的样子,我用Jquery实现了上传。主要分两步:
- 第一步使用Jquery的Ajax请求服务器,拿到上传凭证。
- 第二步使用Jquery的Ajax直接构造一个HTML Form表单,把文件File数据、上传凭证数据等,一起传给阿里云OSS,完成上传。
PS:这里有一个点需要注意,就是你在构造上传表单的时候,务必把文件File这一项放到整个表单数据最后,否则阿里云OSS会报错。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>上传图片</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</head>
<body>
<div>
<form action="" name="upload" method="post" enctype="multipart/form-data">
<input type="file" id="file" name="file">
<button type="button" id="beginUpload">点击开始上传</button>
</form>
</div>
<script>
$(function () {
$("#beginUpload").on("click",function () {
$.ajax({
url:'/upload/webPage2/api',
type:'get',
success:function(res){
//已经获取到上传所需的凭证数据
let uploadBaseData = res;
//开始构造上传表单数据,准备上传
let formData = new FormData();
formData.append("key",uploadBaseData.path);
formData.append("policy",uploadBaseData.policy);
formData.append("OSSAccessKeyId",uploadBaseData.accessId);
formData.append("signature",uploadBaseData.signature);
formData.append("file",$("#file")[0].files[0]);
//开始上传图片到阿里云OSS
$.ajax({
url:'http://' + uploadBaseData.host,
type:'post',
data: formData,
contentType: false,
processData: false,
success:function(res){
console.log(res);
}
})
}
})
});
});
</script>
</body>
</html>
效果演示
上传HTML界面(有点简陋,见谅~)
上传成功后,到阿里云OSS后台查看文件: