通过XHR上传浏览器端的文件资源
最近在为公司开发一个海淘的Chrome扩展,扩展的需求之一是:获取当前页面中的某张图片,并将其上传至公司的服务器保存。
当时的第一思路是:
- 获取图片的URL,然后把URL提交给服务器,让服务器进行下载操作
- 如果服务器访问图片资源时被拒绝,就需要让用户手动下载该图片,然后手动上传至我们的服务器
不过在后来的实践中发现,其实可以直接通过发起XHR请求并指定其返回类型的方式来获取浏览器返回的raw数据(Blob
),然后将这些raw数据当作文件上传至服务器。
在这里首先要说明一点是的:这种将XHR的响应内容当作文件上传的思路,通常是需要跨站请求资源(Cross-site HTTP requests)的,所以除非是在允许跨站请求的运行环境中(如Chrome扩展内)运行这些JS,否则这种思路几乎不可用
###思路
这种上传客户端资源的解决方案最先来自这里upload-a-file-in-a-google-chrome-extension。基本思路是:当使用XHR
向某个URL发起请求时(如请求某个image
、css
、js
文件资源)我们可以设定它的返回类型(responseType
);当我们把返回类型设置为blob
时,我们便可以直接把返回的raw数据当作”文件”来上传到服务器,以此来实现客户端访问资源的上传。
###怎么做 1) 通过XHR请求一个图片资源
var imgURL = 'http://lyfing.qiniudn.com/external_links/pocker_cards_family.png';
var xhr = new XMLHttpRequest();
xhr.open("GET", imgURL, true);
xhr.responseType = "blob";
xhr.onload = function(){
var blob = xhr.response;
var msg = ' Request URL = \n ' + imgURL;
msg += '\n\n Get Response !';
msg += '\n responseType = ' + blob.type;
msg += '\n responseSize = ' + Math.round(blob.size / 1024) + 'KB';
alert(msg);
};
xhr.send();
2) 将返回的Blob对象上传至服务器
// 直接使用上一步获得的blob对象
var blob = xhr1.response;
// 我们可以直接使用FormData构建Form表单
var formData = new FormData();
// 获取文件类型(例如: blob.type = 'image/png')
var fileType = blob.type.split('/')[1];
// 将返回的blob对象直接当作文件上传,并设置上传文件名为 test001.fileType
formData.append('file', blob, 'test001.' + fileType);
var xhr2 = new XMLHttpRequest();
xhr2.upload.onprogress = function(event){
...
$msg.html('正在上传 ( ' + percent + '% )');
}
xhr2.onload = function(){
if ( !confirm('上传完成!即将跳转至上传结果页...') ) return;
// 返回结果是一段纯文本,我们用正则取出本次上传的信息链接
var url = xhr2.response.match(/http:\/\/[^\s]+/)[0];
window.open(url);
}
xhr2.open('POST', 'http://posttestserver.com/post.php?dir=example', true);
xhr2.send(formData);
用到的一些对象
上面我们用到了FormData
对象(详情),它是一个模拟了HTML中的Form表单的实体,你可以直接使用它来构建key/value pair
,其中的value
可以是文件
、Blob
或者纯String
。下面做一个简单的使用示例(Chrome、Firefox可随便使用,IE10+才可使用)
// 创建Form表单对象formData,可在构造函数中传入一个HTML表单对象htmlForm
// 它的键值对会被添加到formData中
var formData = new FormData();
// 添加一个键值对
formData.append('userID', '112233');
// 添加一个文件对象
var file = document.getElementByID('fileField');
formData.append('file1', file);
// 添加一个Blob对象,它将被视为文件上传
formData.append('file2', blob);
关于Blob
对象,它是一个类似于文件
的结构体,事实上文件
接口正是在它的基础上做的扩展。更多关于Blob
备注: