前段web参考地址:
https://www.cnblogs.com/pnz-bug/p/10381839.html
后台java 才考地址:
https://blog.csdn.net/huangxiangdi/article/details/104725048/
1、需求:
想让小程序直接上传图片、文件到OSS存储,不需要经过我的服务器接收文件再转发到OSS存储。
-
可以减轻自己服务器的压力,和不必要的麻烦。
-
有利于维护和扩展(比如扩容、迁移等等)。
-
可以更加快的存储到OSS中,不需要多次来回传输。
-
不占用自己服务器的带宽,阿里的OSS节点处理更快。
-
等等。。。
2、基本原理介绍(官方描述,我觉得很简单了)
服务端签名后前端(小程序)直传的原理如下:
- 用户发送请求到自己服务器获取上传的Policy和签名。
- 自己的服务器返回前端直传oss需要Policy和签名给用户。
- 用户直接上传数据到OSS。
3、springboot 搞起来
1.新建一个springboot 项目,添加依赖
<!--阿里oss-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>2.8.3</version>
</dependency>
2.编写一个OSS的配置类(可以根据自己的需求来,注入等方式都可以)
public class AliOssConfig {
/**
* 填写您的AccessKeyId
*/
public final static String accessId = "xxxx";
/**
* 填写您的AccessKeySecret
*/
public final static String accessKey = "xxxxx";
/**
* 填写您的 endpoint(地域节点),就是你OSS的区域节点的域名
*/
public final static String endpoint = "xxxx.aliyuncs.com";
/**
* 填写您的 bucketName ,就是你再OSS创建Bucket的名称
*/
public final static String bucket = "xxx";
/**
* 直传地址,格式为 bucketName.endpoint (Bucket域名)
*/
public final static String host = "https://" + bucket +"."+ endpoint;
/*
* ============= 配置上传过期时间、大小等等 =========
*/
/**
* 上传截止时间(秒)
*/
public final static long expireTime = 30;
/**
* 上传文件最小(字节)
*/
public final static long min = 0;
/**
* 上传文件最大(字节)
*/
public final static long max = 1048576000;
/**
* 上传文件的前缀、可忽略
*/
public final static String dir = "user/";
}
3.创建controller
@RestController
@RequestMapping("/ali/oss")
public class AliOssController {
/**
* 创建OSS客户端
*/
private OSSClient client = new OSSClient(AliOssConfig.endpoint, AliOssConfig.accessId, AliOssConfig.accessKey);
/**
* 前端获取直传的policy信息
* 前端可以使用响应的参数进行直传到oss存储
*
* @return
*/
@GetMapping("/policy")
public AliOssPolicy getPolicy() {
// 直传有效截止时间
long expireEndTime = System.currentTimeMillis() + (AliOssConfig.expireTime * 1000);
Date expiration = new Date(expireEndTime);
PolicyConditions policyConditions = new PolicyConditions();
// 设置可上传文件的大小
policyConditions.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, AliOssConfig.min, AliOssConfig.max);
// 设置上传文件的前缀、可忽略
policyConditions.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, AliOssConfig.dir);
// 生成policy
String postPolicy = client.generatePostPolicy(expiration, policyConditions);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = client.calculatePostSignature(postPolicy);
// 封装policy等信息
AliOssPolicy aliOssPolicy = new AliOssPolicy();
aliOssPolicy.setOssAccessKeyId(AliOssConfig.accessId);
aliOssPolicy.setPolicy(encodedPolicy);
aliOssPolicy.setSignature(postSignature);
aliOssPolicy.setDir(AliOssConfig.dir);
aliOssPolicy.setHost(AliOssConfig.host);
aliOssPolicy.setExpire(String.valueOf(expireEndTime / 1000));
return aliOssPolicy;
}
}
4.创建一个封装类来响应给前端
public class AliOssPolicy implements Serializable {
/**
* 上传认证id
*/
private String ossAccessKeyId;
/**
* policy
*/
private String policy;
/**
* 签名
*/
private String signature;
/**
* 直传文件的开头(路径)
*/
private String dir;
/**
* 直传地址
*/
private String host;
/**
* 上传截止时间
*/
private String expire;
// get和set省略
}
5、小程序 搞起来
// 调用小程序api选择图片上传
wx.chooseImage({
success: function(res) {
var tempFilePaths = res.tempFilePaths
// 上传图片到oss
wx.uploadFile({
// 直传的oss地址
url: 'https://xxxx.xxxx.aliyuncs.com',
// 上传的文件
filePath: tempFilePaths[0],
// 必须为file
name: 'file',
// 表单数据
formData: {
// key:文件路径,如 user/666.png
//直接 key: "${filename}" 是存放在根目录,文件名为上传的文件名
// 如果后端签名时指定了dir则开头必须同后端的一致,如后端指定了 “user/” 开头则必须是 user/xxx.xx
key: "user/666.png",
// 自定义成功响应值 ,默认响应204
success_action_status: '200',
OssAccessKeyId: 'xxxx',
// 后端签名后发回的policy
policy: 'xxxxxx',
// 后端返回的签名
signature: 'xxxxx'
},
success: function(res) {
if (res.statusCode == 200) {
console.log("上传成功!")
console.log(res)
} else {
console.log("上传失败!")
console.log(res)
}
},
fail(error) {
console.log("上传错误")
console.log(error)
}
})
}
})
1.设置跨域问题请参考阿里OSS 文件
前段javascript例子: web OSS 阿里上传 参考地址:
https://www.cnblogs.com/pnz-bug/p/10381839.html
<!-- 项目源码地址 https://github.com/Pangnz/OssUpload -->
<
br
><!
DOCTYPE
html>
<
html
>
<
head
>
<
meta
charset="utf-8" />
<
title
>web直传oss</
title
>
<
style
type="text/css">
.file-box {
position: relative;
display: inline-block;
width: 160px;
height: 160px;
background: url(img/aaa.png) no-repeat;
background-size: cover;
background-position: center;
}
#input_file {
width: 100%;
height: 100%;
opacity: 0;
filter: alpha(opacity=0);
}
</
style
>
</
head
>
<
body
>
<
p
>oss web服务端签名后直传</
p
>
<
div
class="file-box">
<
input
type="file" value="" name="file" id="input_file" accept="image/gif,image/jpeg,image/jpg,image/png,image/svg"
onchange="imgPreview(this,0)">
</
div
>
<
script
src="js/jquery-3.3.1.min.js" type="text/javascript" charset="utf-8"></
script
>
<
script
type="text/javascript">
let uploadObj; // 上传凭证对象
let verifyImg; //上传之后的图片oss地址
function imgPreview(fileDom, i) {
// 获取上传凭证
$.ajax({
url: '', // 这里是请求的后台通过oss上传凭证的接口
data: JSON.stringify({
data: ''
}),
dataType: 'json',
type: 'post',
headers: {
'Content-Type': 'application/json',
},
success: function(response) {
if (response && response.header.ret == 'S') {
uploadObj = response.data;
// 获取到上传凭证成功之后 封装请求的数据
let request = new FormData();
request.append("OSSAccessKeyId", uploadObj.accessid); //Bucket 拥有者的Access Key Id。
request.append("policy", uploadObj.policy); //policy规定了请求的表单域的合法性
request.append("Signature", uploadObj.signature); //根据Access Key Secret和policy计算的签名信息,OSS验证该签名信息从而验证该Post请求的合法性
//---以上都是阿里的认证策略
let file = document.getElementById("input_file").files[0];
if (!file) {
return;
}
request.append("key", `${uploadObj.dir}/${file.name}`); //文件名字,可设置路径
request.append("success_action_status", '200'); // 让服务端返回200,不然,默认会返回204
request.append('file', file); //需要上传的文件 file
$.ajax({
url: uploadObj.host, //上传阿里地址
data: request,
processData: false, //默认true,设置为 false,不需要进行序列化处理
cache: false, //设置为false将不会从浏览器缓存中加载请求信息
async: false, //发送同步请求
contentType: false, //避免服务器不能正常解析文件---------具体的可以查下这些参数的含义
dataType: 'json', //不涉及跨域 写json即可
type: 'post',
success: function(response) { //callbackHost:success,request中就是 回调的一些信息,包括状态码什么的
// console.log(response);
},
error: function(error) {
alert("上传图片成功");
verifyImg = `${uploadObj.host}/${uploadObj.dir}/${file.name}`;
}
});
} else {
alert(`${response && response.header.msg[0]}`);
}
},
error: function(error) {
console.log(error);
},
});
console.log(`上传的文件oss地址${verifyImg}`);
//判断是否支持FileReader
if (window.FileReader) {
var reader = new FileReader();
} else {
alert("您的设备不支持图片预览功能,如需该功能请升级您的设备!");
}
//获取文件
var file = fileDom.files[0];
if (!file) {
return;
}
var imageType = /^image\//;
//是否是图片
if (!imageType.test(file.type)) {
alert("请选择图片!");
return;
}
//读取完成
reader.onload = function(e) {
//图片路径设置为读取的图片
// img.src = e.target.result;
document.getElementsByClassName('file-box')[i].style.background = "url(" + e.target.result + ")no-repeat"; //回显图片
document.getElementsByClassName('file-box')[i].style.backgroundSize = '';
};
reader.readAsDataURL(file);
}
</
script
>
</
body
>
</
html
>