Vue获取服务端签名web端直传OSS,各种报错The bucket POST must contain the specified ‘key‘.等解决办法
目录项目场景:前端代码Java后台授权代码总结项目场景:文件上传阿里云OSS,通常情况下的上传方式是页面先文件上传到我们的后台服务器,我们的后台服务器在上传到OSS,这样的话一个文件的上传操作就相当于消耗了两份服务器带宽,流程图如图所示:而如果项目的文件或者图片上传业务比较大的话,显然这对服务器来说是一个不必要的开销。当然,我们也有优化的解决办法,通过服务端给我们生成一个允许我们上传文件到OSS的
项目场景:
文件上传阿里云OSS,通常情况下的上传方式是页面先文件上传到我们的后台服务器,我们的后台服务器在上传到OSS,这样的话一个文件的上传操作就相当于消耗了两份服务器带宽,
流程图如图所示:
而如果项目的文件或者图片上传业务比较大的话,显然这对服务器来说是一个不必要的开销。
当然,我们也有优化的解决办法,通过服务端给我们生成一个允许我们上传文件到OSS的签名,我们前端项目拿到这个签名去直接上传到OSS,这样就减少了额外的带宽开销,流程图如下所示:
具体的官方信息描述可以点击这里查看:
https://help.aliyun.com/document_detail/31927.html?spm=a2c4g.11186623.6.1744.17e03bd38ehYZz
Vue获取服务端签名web端直传OSS
<template>
<div class="test">
<div>
<input type="file" id="file" name="file" />
<a @click="upload()" href="javascript:;">上传</a>
</div>
</div>
</template>
<script>
export default {
data(){
return {
}
},
mounted() {
this.getOssToken();
},
methods: {
//获取上传通行证
getOssToken(){
var _self = this;
axios.get('/api/oss/getPolicy').then(function(res){
console.log(res);
if(res.data.code == 200){
_self.aliyunOssToken = res.data.data;
}else{
_self.$message.error(res.data.message);
}
}).catch(function(error){
console.log(error);
})
},
upload(){
var _self = this;
var getSuffix = function (fileName) {
var pos = fileName.lastIndexOf(".");
var suffix = '';
if (pos != -1) {
suffix = fileName.substring(pos);
}
return suffix;
}
var file = $("#file").val();
if (file.length == 0) {
alert("请选择文件");
}
var filename = new Date().getTime() + getSuffix(file);
var formData = new FormData();
//注意formData里append添加的键的大小写
formData.append('key', _self.aliyunOssToken.dir + filename); //存储在oss的文件路径
formData.append('OSSAccessKeyId', _self.aliyunOssToken.accessid); //accessKeyId
formData.append('policy', _self.aliyunOssToken.policy); //policy
formData.append('Signature', _self.aliyunOssToken.signature); //签名
//如果是base64文件,那么直接把base64字符串转成blob对象进行上传就可以了
formData.append("file", $("#file")[0].files[0]);
formData.append('success_action_status', 200); //成功后返回的操作码
var url = _self.aliyunOssToken.host;
var fileUrl = _self.aliyunOssToken.host +'/'+ _self.aliyunOssToken.dir + filename;
$.ajax({
url: url,
type: 'POST',
data: formData,
// async: false,
cache: false,
contentType: false,
processData: false,
success: function (data) {
console.log(fileUrl);
console.log(data);
},
error: function (data) {
console.log(data);
}
});
}
}
}
</script>
这里需要注意的是:
如果你遇到了
<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Error>\n <Code>InvalidArgument</Code>\n <Message>The bucket POST must contain the specified 'key'. If it is specified, please check the order of the fields</Message>\n <RequestId>60950F076AD6D5
这种错误的话,那么可能,是你的key字段不存在,这里所有的字段名称一定要和官方文档给定的一致,否则就会报错,注意大小写。
如果不是的话,那么你可能就是遇到了跟我一样的错误,如图所示:
查阅官方文档后,官方文档给出的解释是这样的,点击查看:
如图所示:
如果还有其他错误,可对照官方文档一步步排查。
Java后台授权代码
这里我也给贴出来吧,省得你再去翻看官方文档了:
有点多,你就看你有用的就行:
package cc.mrbird.febs.external.oss;
import cc.mrbird.febs.common.entity.FebsResponse;
import cc.mrbird.febs.common.exception.FebsException;
import cc.mrbird.febs.common.utils.RandomUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.*;
import lombok.Data;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @ Author 马超伟
* @ Date 2021-04-30 09:21
* @ Description:
* @ Version:
*/
@Data
@Service
public class OssFileUpload {
@Value("${aliyun.oss.file.endpoint}")
private String endpoint;
@Value("${aliyun.oss.file.keyid}")
private String accessKeyId;
@Value("${aliyun.oss.file.keysecret}")
private String accessKeySecret;
@Value("${aliyun.oss.file.filehost}")
private String fileHost;
@Value("${aliyun.oss.file.bucketname}")
private String bucketName;
@Value("${aliyun.oss.file.callbackUrl}")
private String callbackUrl;
@Value("${aliyun.oss.file.baseDir}")
private String dir;
public OSS getOssClient() {
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
return ossClient;
}
/**
* @ Author: 马超伟
* @ Date: 2021/4/30 10:59
* @ Params: [file, basePath]
* @ return: java.lang.String
* @ Description: 文件上传
*/
public String upload(MultipartFile file, String basePath) {
String uploadUrl;
try {
//判断oss实例是否存在:如果不存在则创建,如果存在则获取
OSS ossClient = getOssClient();
//获取上传文件流
InputStream inputStream = file.getInputStream();
//构建日期路径:avatar/2019/02/26/文件名
String filePath = new DateTime().toString("yyyy/MM/dd");
//文件名:uuid/原始文件名到后缀
String original = file.getOriginalFilename();
String fileName = RandomUtil.getLinkNo();
String newName = fileName + "/" + original;
String fileUrl = basePath + "/" + filePath + "/" + newName;
//文件上传至阿里云
ossClient.putObject(bucketName, fileUrl, inputStream);
// 关闭OSSClient。
ossClient.shutdown();
//获取url地址
uploadUrl = fileHost + "/" + fileUrl;
} catch (IOException e) {
throw new FebsException("文件上传异常!");
}
return uploadUrl;
}
/**
* @return List<String> 文件路径和大小集合
* @ Param ossClient oss客户端
* @ Param bucketName bucket名称
* @Title: queryAllObject
* @ Description: 查询某个bucket里面的所有文件
*/
public List<String> queryAllObject() {
List<String> results = new ArrayList<>();
try {
// ossClient.listObjects返回ObjectListing实例,包含此次listObject请求的返回结果。
ObjectListing objectListing = getOssClient().listObjects(bucketName);
// objectListing.getObjectSummaries获取所有文件的描述信息。
for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
results.add(" - " + objectSummary.getKey() + " " + "(size = " + objectSummary.getSize() + ")");
}
} catch (Exception e) {
e.printStackTrace();
}
return results;
}
/**
* 从阿里云下载单个文件
*
* @ Param objectName
*/
public void download(String objectName, HttpServletResponse response) {
BufferedInputStream input = null;
OutputStream outputStream;
OSSObject ossObject;
try {
objectName = objectName.replace(fileHost + "/", "");
response.reset();
response.setCharacterEncoding("utf-8");
response.setContentType("application/x-msdownload");
String substring = objectName.substring(objectName.lastIndexOf("/") + 1);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(substring, "UTF-8"));
// ossObject包含文件所在的存储空间名称、文件名称、文件元信息以及一个输入流。
ossObject = getOssClient().getObject(bucketName, objectName);
input = new BufferedInputStream(ossObject.getObjectContent());
byte[] buffBytes = new byte[1024];
outputStream = response.getOutputStream();
int read;
while ((read = input.read(buffBytes)) != -1) {
outputStream.write(buffBytes, 0, read);
}
input.close();
ossObject.close();
outputStream.close();
} catch (IOException ex) {
ex.printStackTrace();
} finally {
try {
if (input != null) {
input.close();
}
} catch (IOException e) {
// e.printStackTrace();
}
}
}
public void downloadObject(String objectName) {
// 创建OSSClient实例。
OSS ossClient = getOssClient();
// 下载Object到本地文件,并保存到指定的本地路径中。如果指定的本地文件存在会覆盖,不存在则新建。
// 如果未指定本地路径,则下载后的文件默认保存到示例程序所属项目对应本地路径中。
objectName = objectName.replace(fileHost + "/", "");
ossClient.getObject(new GetObjectRequest(bucketName, objectName), new File("D:\\localpath\\"+objectName));
// 关闭OSSClient。
ossClient.shutdown();
}
/**
* 文件及文件夹的递归删除
*
* @ Param file
*/
private static void deleteFile(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
assert files != null;
for (File f : files) {
deleteFile(f);
//将循环后的空文件夹删除
if (f.exists()) {
f.delete();
}
}
} else {
file.delete();
}
}
/**
* @ Author: 马超伟
* @ Date: 2021/5/7 18:16
* @ Params: []
* @ return: cc.mrbird.febs.common.entity.FebsResponse
* @ Description: 前台获取文件上传签名授权
*/
public FebsResponse policy() {
//https://gulimall-hello.oss-cn-beijing.aliyuncs.com/hahaha.jpg
// callbackUrl为 上传回调服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
// String callbackUrl = "http://88.88.88.88:8888";
String filePath = new DateTime().toString("yyyy/MM/dd");
String dir = "file/" + filePath; // 用户上传文件时指定的前缀。
Map<String, String> respMap = null;
try {
long expireTime = 30;
long expireEndTime = System.currentTimeMillis() + expireTime * 1000;
Date expiration = new Date(expireEndTime);
PolicyConditions policyConds = new PolicyConditions();
policyConds.addConditionItem(PolicyConditions.COND_CONTENT_LENGTH_RANGE, 0, 1048576000);
policyConds.addConditionItem(MatchMode.StartWith, PolicyConditions.COND_KEY, dir);
String postPolicy = getOssClient().generatePostPolicy(expiration, policyConds);
byte[] binaryData = postPolicy.getBytes(StandardCharsets.UTF_8);
String encodedPolicy = BinaryUtil.toBase64String(binaryData);
String postSignature = getOssClient().calculatePostSignature(postPolicy);
respMap = new LinkedHashMap<>();
respMap.put("accessid", accessKeyId);
respMap.put("policy", encodedPolicy);
respMap.put("signature", postSignature);
respMap.put("dir", dir);
respMap.put("host", fileHost);
respMap.put("expire", String.valueOf(expireEndTime / 1000));
// respMap.put("expire", formatISO8601Date(expiration));
} catch (Exception e) {
// Assert.fail(e.getMessage());
System.out.println(e.getMessage());
}
return new FebsResponse().success().put("data", respMap);
}
}
总结
遇到问题并不可怕,可怕的是找不出来问题在哪, 这种调用人家方法的,就按照人家的要求一步步走就行了,自己的任何‘**投机取巧、标新立异’**都是行不通的。找到报错信息,去官方文档查阅‘常见问题’,解决起来就轻松多了。
更多推荐
所有评论(0)