将一个图片地址转成文件流(File)再上传
写在开头
最近,小编在业务中遇到一个图片转存的场景。
领导🤠:大概过程就是,接口会给我返回一个图片列表数据,图片路径是全路径,但是路径中的域名是其他系统的,必须要在用户选择图片的时候将图片重新转存到自个的系统上,防止其他系统删除图片对此有影响。
我😃:Em...很合理的需求。
(但是,和有什么关系?我只是一个前端小菜鸡呀,不祥的预感.......)
我😃:(卑微提问)这个过程不是放后端做比较合理一点?
后端大哥😡:前端不能做?
我😣:可以可以,只是...这个好像会跨域?
后端大哥😠:已经配置了请求头('Access-Control-Allow-Origin': '*'
)。
我😖:哦,好的,我去弄一下。(*******此处省略几万字心理活动内容)
第一种(推荐)
那么,迫于......不,我自愿的,我们来看看前端要如何完成这个转成过程,代码比较简单,直接贴上来瞧瞧:
async function imageToStorage(path) {
// 获取文件名
const startIndex = path.lastIndexOf('/');
const endIndex = path.indexOf('?');
const imgName = path.substring(startIndex + 1, endIndex);
// 获取图片的文件流对象
const file = await getImgToFile(path, imgName);
// TODO: 将File对象上传到其他接口中
}
/**
* @name 通过fetch请求文件,将文件转成文件流对象
* @param { string } path 文件路径全路径
* @param { string } fileName 文件名
* @returns { File | undefined }
*/
function getImgToFile(path, fileName) {
const response = await fetch(path);
if (response) {
const blob = await response.blob();
const file = new File([blob], fileName, { type: blob.type });
return file;
}
}
上述方式,在后端配置了允许跨域后,正常是没有什么问题的,也是比较好的一种方式了。😃
但是,在小编实际第一次编码测试后,却还是遇上了跨域。😓
一猜应该就是后端实际还没配置好,问了一下。
后端大哥😑:还没部署,一会再自己试试。
我😤:嗯嗯。
第二种
等待的过程,小编又在网上找了找了,找到了第二种方式,各位看官可以瞧瞧:
/** @name 将图片的网络链接转成base64 **/
function imageUrlToBase64(imageUrl: string, fileName: string): Promise<File> {
return new Promise(resolve => {
const image = new Image();
// 让Image元素启用cors来处理跨源请求
image.setAttribute('crossOrigin', 'anonymous');
image.src = imageUrl + '&v=' + Math.random();
image.onload = () => {
const canvas = document.createElement('canvas');
canvas.width = image.width;
canvas.height = image.height;
const context = canvas.getContext('2d')!;
context.drawImage(image, 0, 0, image.width, image.height);
// canvas.toDataURL
const imageBase64 = canvas.toDataURL('image/jpeg', 1); // 第二个参数是压缩质量
// 将图片的base64转成文件流
const file = base64ToFile(imageBase64, fileName);
resolve(file);
};
});
}
/** @name 将图片的base64转成文件流 **/
function base64ToFile(base64: string, fileName: string) {
const baseArray = base64.split(',');
// 获取类型与后缀名
const mime = baseArray[0].match(/:(.*?);/)![1];
const suffix = mime.split('/')[1];
// 转换数据
const bstr = atob(baseArray[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
// 生成文件流
const file = new File([u8arr], `${fileName}.${suffix}`, {
type: mime,
});
return file;
}
这第二种方式由于要先把图片绘制到 canvas
再去转成 base64
再去转成文件流,小编用 console.time
稍微测了一下,每次转化过程都要几百毫秒,图片越大时间越长,挺影响性能的。
所以,小编还是推荐使用第一种方式,当然,最稳妥的方案是后端去搞最好了。😉
网上很多都说第二种方式可以直接绕过跨域,各种谈论。😪
主要就是这个
crossOrigin
属性。MDN解释它原理是通过了 CORS
或者可以再看看这个解释:传送门
至此,本篇文章就写完啦,撒花撒花。
希望本文对你有所帮助,如有任何疑问,期待你的留言哦。
老样子,点赞+评论=你会了,收藏=你精通了。
来源:juejin.cn/post/7336756027385872424