解决方案(我封装的上传代码在后面“封装上传”部分):
就直接上阿里oss管理后台去增加一个跨域规则:见图片,特详细
配置成这样点确定就好了,就这么简单
案发背景:
标题其实就已经是答案了,但是以我的性格,我还是要介绍介绍案发背景,如果你比较着急,可以直接跳到解决方案那里看:
本来我就是个干前端的,也干安卓端(为啥不干ios,因为没钱买mac),然后我只管vue、js这一亩三分地,什么服务器配置,阿里云oss,跟我都没啥关系。直到我们老板用我们的网站,上传大文件的时候报错,后端测试说没问题(后端是用小文件测的,这个文件本来是老板发的,但是老板微信发出来以后,可能就压缩了很多,导致他上传成功了),然后我用大视频上传了一下,发现报了超时错误,我们用的上传是后端写的上传接口,接口报错理论上应该接口解决,但是接口可能解决了很久也不太好解决这个问题,时间久了没改好,老板就说以后上传就都用前端直传oss,这不就得鄙人上了?
然后就遇到了标题说的跨域问题,解释一下:
这个错误是由于CORS(跨源资源共享)策略引起的。CORS是一种浏览器的安全机制,用于限制跨域请求,防止恶意网站发送请求来获取敏感数据。当浏览器向一个不同于当前域的域名发起请求时,服务器需要设置CORS响应头来允许跨域请求。
Access to XMLHttpRequest at 'xxx' from origin 'http://localhost:8023' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
我查阅百度后跟后端说,你需要这样这样配置一下,然后他配了半天,说好了,我说还是跨域,后端说,我这儿没错呀,我都登录上去给你配置了 ,你用这个js demo试试,他发给我一个plupload demo,运行起来我发现真的可以上传,我想那应该是我的问题,然后我看他的运行效果,是挺不错,还能监听上传进度,诶等等,上传进度?那不是分片上传才有的么,那分片上传确实能规避跨域检测,因为每片的大小不足以触发浏览器的同源检测。合着并不是解决了问题,而是后端让我兜个大圈子实现上传。我想能传就行,管他方法呢,然后我一看plupload代码,我滴天,真是可怕,要是以后都用这种方法,那我不得难受死,一他只适合原生,对vue的这种双向绑定模式完全不友好,因为他甚至都要传递标签id作为参数进去,这要是每个上传都用,头得疼死,给你们看看这部分plupload代码,这段代码可以精简一部分,但是仍然很麻烦,精简不了太多,你们感兴趣可以看看:
import plupload from 'plupload';
let accessid = ''
let accesskey = ''
let host = ''
let policyBase64 = ''
let signature = ''
let callbackbody = ''
let filename = ''
let key = ''
let expire = 0
let g_object_name = ''
let g_object_name_type = ''
let timestamp = ''
let now = timestamp = Date.parse(new Date()) / 1000;
function send_request() {
var xmlhttp = null;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp != null) {
// serverUrl是 用户获取 '签名和Policy' 等信息的应用服务器的URL,请将下面的IP和Port配置为您自己的真实信息。
let serverUrl = 'xxx'
xmlhttp.open("GET", serverUrl, false);
xmlhttp.send(null);
return xmlhttp.responseText
} else {
alert("Your browser does not support XMLHTTP.");
}
}
function get_signature() {
// 可以判断当前expire是否超过了当前时间, 如果超过了当前时间, 就重新取一下,3s 作为缓冲。
now = timestamp = Date.parse(new Date()) / 1000;
if (expire < now + 3) {
let body = send_request()
var obj = eval("(" + body + ")");
host = obj['host']
policyBase64 = obj['policy']
accessid = obj['accessid']
signature = obj['signature']
expire = parseInt(obj['expire'])
callbackbody = obj['callback']
key = obj['dir']
return true;
}
return false;
}
function calculate_object_name(filename) {
// if (g_object_name_type == 'local_name') {
// g_object_name += "${filename}"
// } else if (g_object_name_type == 'random_name') {
let suffix = get_suffix(filename)
g_object_name = key + random_string(10) + suffix
// }
return ''
}
function get_uploaded_object_name(filename) {
if (g_object_name_type == 'local_name') {
let tmp_name = g_object_name
tmp_name = tmp_name.replace("${filename}", filename);
return tmp_name
} else if (g_object_name_type == 'random_name') {
return g_object_name
}
}
function set_upload_param(up, filename, ret) {
if (ret == false) {
ret = get_signature()
}
g_object_name = key;
if (filename != '') {
let suffix = get_suffix(filename)
calculate_object_name(filename)
}
let new_multipart_params = {
'key': g_object_name,
'policy': policyBase64,
'OSSAccessKeyId': accessid,
'success_action_status': '200', //让服务端返回200,不然,默认会返回204
'callback': callbackbody,
'signature': signature,
};
up.setOption({
'url': host,
'multipart_params': new_multipart_params
});
up.start();
}
function check_object_radio() {
var tt = document.getElementsByName('myradio1');
for (var i = 0; i < tt.length; i++) {
if (tt[i].checked) {
g_object_name_type = tt[i].value;
break;
}
}
}
function random_string(len) {
len = len || 32;
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
var maxPos = chars.length;
var pwd = '';
for (var i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
function get_suffix(filename) {
let pos = filename.lastIndexOf('.')
let suffix = ''
if (pos != -1) {
suffix = filename.substring(pos)
}
return suffix;
}
/**
* 初始化上传插件
*/
function initPlUploader(lthis) {
var uploader = new plupload.Uploader({
runtimes: "html5,flash,silverlight,html4",
browse_button: "courseSelectFile",
//multi_selection: false,
// container: document.getElementById('container'),
// flash_swf_url : 'lib/plupload-2.1.2/js/Moxie.swf',
// silverlight_xap_url : 'lib/plupload-2.1.2/js/Moxie.xap',
url: "http://oss.aliyuncs.com",
filters: {
mime_types: [
//只允许上传图片和zip文件
{title: "Video files", extensions: "mp4,avi,wmv,mov,flv,rmvb,3gp,m4v,mkv"},
// { title: "Zip files", extensions: "zip,rar,ipa" }
],
max_file_size: "10000mb", //最大只能上传10mb的文件
prevent_duplicates: false //不允许选取重复文件
},
init: {
PostInit: function () {
document.getElementById("ossfile").innerHTML = "";
document.getElementById("postfiles").onclick = function () {
set_upload_param(uploader, "", false);
return false;
};
},
FilesAdded: function (up, files) {
plupload.each(files, function (file) {
document.getElementById("ossfile").innerHTML +=
'<div id="' +
file.id +
'">' +
file.name +
" (" +
plupload.formatSize(file.size) +
")<b></b>" +
'<div class="progress"><div class="progress-bar" style="width: 0%"></div></div>' +
"</div>";
});
document.getElementById("postfiles").click()
},
BeforeUpload: function (up, file) {
check_object_radio();
set_upload_param(up, file.name, true);
lthis.subsectionFormValidate.subsectionName = file.name.substring(0, file.name.lastIndexOf("."))
},
UploadProgress: function (up, file) {
var d = document.getElementById(file.id);
d.getElementsByTagName("b")[0].innerHTML =
"<span>" + file.percent + "%</span>";
var prog = d.getElementsByTagName("div")[0];
var progBar = prog.getElementsByTagName("div")[0];
progBar.style.width = 2 * file.percent + "px";
progBar.setAttribute("aria-valuenow", file.percent);
},
FileUploaded: function (up, file, info) {
if (info.status == 200) {
// document
// .getElementById(file.id)
// .getElementsByTagName("b")[0].innerHTML =
// "upload to oss success, object name:" +
// get_uploaded_object_name(file.name) +
// " 回调服务器返回的内容是:" +
// info.response;
lthis.subsectionFormValidate.videos.push(JSON.parse(info.response))
lthis.$Message.success('视频上传成功')
lthis.save(3)
} else if (info.status == 203) {
document
.getElementById(file.id)
.getElementsByTagName("b")[0].innerHTML =
"上传到OSS成功,但是oss访问用户设置的上传回调服务器失败,失败原因是:" +
info.response;
} else {
document
.getElementById(file.id)
.getElementsByTagName("b")[0].innerHTML = info.response;
}
},
Error: function (up, err) {
if (err.code == -600) {
lthis.$Message.error('文件最大不得超过10G')
} else if (err.code == -601) {
lthis.$Message.error('请上传要求的视频格式文件')
} else if (err.code == -602) {
lthis.$Message.error('这个文件上传过了')
} else {
lthis.$Message.error('错误:' + err.response)
}
}
}
});
uploader.init();
}
这要是每一处上传都用这个方法,以我有限的能力,那得把我累死。然后我要求后端继续修改oss配置,后端说:好,我看看,好的我一会儿上去看看,诶我记得你之前项目不是上传成功了么......,第二天,仍然未果,我跟老板要了oss密码账号登录上去自己改了配置(一直以来都是后端来处理服务器相关的配置),诶,前端代码一点没动,上传成功了,
下面是我自己的上传部分代码
封装上传:
我用阿里oss文档去整理,让后端写获取临时秘钥接口,这些都完成了,我封装了一个js工具,也给大家贡献出来吧,看注释就行,我的get方法就是axios里的get方法,你直接用axios.get也是可以的
import OSS from 'ali-oss'; import { Toast } from '_vant@2.12.54@vant'; import {$get} from "./http"; // 从后端服务获取 STS 临时访问凭证,这个让后端照着阿里云的文档写,拿到一些keyid和临时秘钥啥的 async function getOSSSTS() { const res = await $get('/...这里写你自己的接口路径/getOssSTS', { params: { type: 2 } }); if (res.code === 200) { return res.data; } else { Toast(res.msg); } } // 创建阿里云 OSS 客户端实例 async function createOSSClient() { const ossSTS = await getOSSSTS(); return new OSS({ region: ossSTS.region, accessKeyId: ossSTS.accessKeyId, accessKeySecret: ossSTS.accessKeySecret, stsToken: ossSTS.stsToken, bucket: ossSTS.bucket }); } // 上传文件到阿里云 OSS export async function uploadToOSS(file) { const client = await createOSSClient(); const result = await client.put(file.name, file); return result.url; }
然后这里是调用,都是用async/await的同步化写法来处理异步请求,类似于协程的感觉,这是es7的语法。
this.video = await uploadToOSS(file.file);
这里配置了上传成功以后直接返回文件地址url
到这里就可以打他们的脸了。如果帮到了你,那不胜荣幸,感谢关注。