这两天收到一个很紧急的需求,需要在一天后上线。需求是要在app上面实现一个反馈的表单,表单里面有上传视频的功能。讲道理,刚收到这个需求的时候,觉得应该不难。但是在实现的过程中感觉还是有点曲折,于是在把这个项目交付完了以后,写下来,记录一些心得。
寻找调用可以拿到视频的api
开始动手的时候,在交互库里寻找了一番,发现了一个很严重的问题:交互库这边调用图库的方法,只能拿到图片。这就很难受了呀,于是开始寻找可以拿到图库的api。起初,第一个想到的是
<input type = “file” />
因为 input 标签在里面有个api=>type,这个是可以获取到文件的类型,是可以拿到视频的。于是在浏览器上面尝试了一下,发现果然是可以,于是在项目app里面搞了一下测试页面,发现了问题:ios可以看到视频,安卓的不行。 裂开了啊。
于是想着换其他api。之前在其他页面试过有赞的组件,感觉还可以。而且里面也有上传的组件。于是,也测试了一下。发现了一样的问题:ios可以看到视频,安卓的不行,看了一下文档,有赞用的也是原生的 input 标签,裂开了啊。我在其他的app上面尝试了一下,安卓和ios都是可以拿到视频的,那就是结论只有一个:我们的项目app没有打开这个对应的权限。那没办法了啊,赶紧向产品经理抛开问题,app改动再上集成是来不及的,产品经理已同意,并说后续在让app迭代这个方面的功能,并说样式方面我相信你,那我必然优先选择有赞了!
开始开发,第一个版本(以base64格式上传视频)
首先总结一下这个开发的流程,上传视频到七牛之前,要先从后端哪里拿到一个对象,对象里面有 { token,url },token赋值给你要上传的文件接口上面,url是上传地址,然后上传到七牛。第一次,后端给的上传接口是之前上传图片到七牛的接口,是将图片转为base64上传。
这是一个gulp的的项目,之前有仙人在这里面大胆的引用了vue,于是乎,我也不敢乱调整,就在里面使用vue开发了。
引入有赞方法这里不做赘述,放上有赞快速上手的地址:
[youzan.github.io/vant/#/zh-C…]
上传视频的HTML:
<van-uploader v-model="fileList" accept="image/*,video/*" max-count='3' :after-read="afterRead" multiple />
// accept 接受的类型
// max-count 最大上传文件数
// after-read 读取文件后的回调
写一个获取token的回调函数:
getToken(typeObj, success, fail) {
const param = {
url: `${baseUrl}/file/getUpload/`, // 上传的地址
method: "post",
body: JSON.stringify({ // 对应的参数
filename:“”
}),
};
xxxxBridge.request(param, (res) => {
// console.info("getToken.res=", res);
if (res.code === "xxxxxx") {
success && success(res.data);
} else {
fail && fail(res);
}
});
},
上传到七牛:
有赞的组件提供了一个afterRead的函数,可以拿到文件,以及base64码还是有点方便的
afterRead(file) {
file.status = "uploading";
file.message = "上传中...";
vm.getToken(
vm.getType(file.file),
function (data) {
if (!data) {
return xxxxBridge.toast("没有获取到文件的token");
}
const url = data.url + "/putb64/xxxxxx" || "https://xxxx.qiniup.com/putb64/xxxxxx;
const xhr = new XMLHttpRequest();
const base64Filter = vm.base64Filter(file.content) // 获取base64码后面逗号以后的码
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
file.status = "done";
file.message = "已上传";
xxxxBridge.jsLog(xhr.responseText);
const response = JSON.parse(xhr.responseText);
// console.log('response:'+ JSON.stringify(response))
}
};
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/octet-stream");
xhr.setRequestHeader("Authorization", "UpToken " + data.token);
xhr.send(base64Filter);
},
function (error) {
console.error(error);
xxxxBridge.toast("上传失败");
}
);
},
base64Filter(str){
const base64Arr = str.split(',')
return base64Arr[1]
},
测试一下,成功的。但是上传的属实是 有点慢,我上传了一个30M大小的视频,差不多需要1分钟30秒。
输出了上传一下文件,打了一下日志,发现转为base64码后大小增加了原体积的一半,这属实是有点大啊。
base64码大小:
视频实际大小:
第二个版本(表单形式上传)
接着和后端讨论了一下,用表单的形式上传视频。但是测试表单的过程中,发现一直请求不通服务器。找了很久原因,才发现交互库的请求方法竟然不支持传表单。很难受,之前的封装的请求方法的表头和上次表单的表单不一样,重新再封装一个请求方法
function sendAjaxQiNiu(options) {
var request = new XMLHttpRequest();
request.open(options.method, options.url);
if (!options.headers) {
request.setRequestHeader('Content-Type', 'application/json');
}
request.send(options.data);
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status >= 200 && request.status < 300) {
var data = JSON.parse(request.response);
var headers = request.getAllResponseHeaders();
options.success && options.success(data, headers);
} else if (request.status >= 400) {
options.fail && options.fail(request);
}
}
};
}
获取token,上传
afterRead(file) {
file.status = "uploading";
file.message = "上传中...";
var token = '';
var p = {
url: `${baseUrl}/smartwatch/xxxxxxxxxxxxx`,
method: 'GET',
}
xxxxBridge.request(p, function (res) {
if (res.code === 'xxxxxx') {
token = res.data.uploadToken;
var formData = new FormData();
formData.append('file', file.file);
formData.append('token', token);
sendAjaxQiNiu({
url: `https://upload.qiniup.com/xxxxxxxxxxx`,
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data' // 注意:这是上传表单的表头
},
data: formData,
success: function (res) {
file.status = "done";
file.message = "已上传";
const response = res
},
})
}
}, )
},
ok,上传效率比之前明显快很多。大家有其他可以拿到视频的方法,欢迎大家给点建议。
补说一句,千万不要接紧急的需求。特别是原先不是你开发的项目,并且交互库不支持的情况下,要兼容很多东西。看似这样上面这样轻描淡写,真的联调时间,在加上其他样式和测试时间乱七八遭,一套流程下来,加班是必然的