文件异步上传

摘要:
varfileInput=document.getElementById(“myFile”);读者readAsDataURL(文件);并传递给onload回调函数中设置为img的src。此外,我们可以预览varimg=document。通过使用对象URL创建元素(“img”);img.src=window.URL.createObjectURL(文件);

本文摘抄自网络

XHR对象还有一个属性upload, 它返回一个XMLHttpRequestUpload 对象,这个对象拥有下列下列方法:

  • onloadstart

  • onprogress

  • onabort

  • onerror

  • onload

  • ontimeout

  • onloadend

这些方法在XHR对象中都存在同名版本,区别是后者是用于加载资源时,而前者用于资源上传时。其中onprogress 事件回调方法可用于跟踪资源上传的进度,它的event参数对象包含两个重要的属性loaded和total。分别代表当前已上传的字节数(number of bytes)和文件的总字节数。比如我们可以这样计算进度百分比:

xhr.upload.onprogress = function(event) {
    if (event.lengthComputable) {
        var percentComplete = (event.loaded / event.total) * 100;
        // 对进度进行处理
    }
}

其中事件的lengthComputable属性代表文件总大小是否可知。如果 lengthComputable 属性的值是 false,那么意味着总字节数是未知并且 total 的值为零。

如果是现代浏览器,可以直接配合HTML5提供的

<progress id="myProgress" value="50" max="100">
</progress>

其value属性绑定上面代码中的percentComplete的值即可。再进一步我们还可以对<progress>的样式统一调整,实现优雅降级方案,具体参见这篇文章

再说说我在测试这个progress事件时遇到的一个问题。一开始我设在onprogress事件回调里的断点总是只能走到一次,并且loaded值始终等于total。觉得有点诡异,改用console.log打印loaded值不见效,于是直接加大上传文件的大小到50MB,终于看到了5个不同的百分比值。

因为xhr.upload.onprogress在上传阶段(即xhr.send()之后,xhr.readystate=2之前)触发,每50ms触发一次。所以文件太小网络环境好的时候是直接到100%的。

关于文件预览,现代浏览器,使用HTML5的FileReader API可以实现。

function handleImageFile(file) {
       var previewArea = document.getElementById('previewArea');
       var img = document.createElement('img');
       var fileInput = document.getElementById("myFile");
       var file = fileInput.files[0];
       img.file = file;
       previewArea.appendChild(img);

       var reader = new FileReader();
       reader.onload = (function(aImg) {
            return function(e) {
                 aImg.src = e.target.result;
            }
       })(img);
       reader.readAsDataURL(file);
}

这里我们使用FileReader来处理图片的异步加载。

在创建新的FileReader对象之后,我们建立了onload函数,然后调用readAsDataURL()开始在后台进行读取操作。当图像文件加载后,转换成一个 data: URL,并传递到onload回调函数中设置给img的src。

另外我们还可以通过使用对象URL来实现预览

var img = document.createElement("img");
img.src = window.URL.createObjectURL(file);;
img.onload = function() {
    // 明确地通过调用释放
    window.URL.revokeObjectURL(this.src);
}
previewArea.appendChild(img);

二进制上传

XMLHttpRequest.prototype.sendAsBinary = function(text){
    var data = new ArrayBuffer(text.length);
    var ui8a = new Uint8Array(data, 0);
    for (var i = 0; i < text.length; i++){ 
        ui8a[i] = (text.charCodeAt(i) & 0xff);
    }
    this.send(ui8a);
}

因为在现代浏览器中我们可以用XMLHttpRequest Level 2来支持二进制数据,异步文件上传,并且动态创建FormData。

而低版本的IE里的XMLHttpRequest是Level 1。所以我们通过XHR异步向服务器发上传请求的路走不通了。只能老老实实的用form的submit。

而form的submit会导致页面的刷新。原因分析好了,那么答案就近在咫尺了。我们能不能让form的submit不刷新整个页面呢?答案就是利用iframe。把form的target指定到一个看不见的iframe,那么返回的数据就会被这个iframe接受,于是乎就只有这个iframe会刷新。而它又是看不见的,用户自然就感知不到了。

window.__iframeCount = 0;
var hiddenframe = document.createElement("iframe");
var frameName = "upload-iframe" + ++window.__iframeCount;
hiddenframe.name = frameName;
hiddenframe.id = frameName;
hiddenframe.setAttribute("style", "0;height:0;display:none");
document.body.appendChild(hiddenframe);

var form = document.getElementById("myForm");
form.target = frameName;

然后响应iframe的onload事件,获取response

hiddenframe.onload = function(){
    // 获取iframe的内容,即服务返回的数据
    var resData = this.contentDocument.body.textContent || this.contentWindow.document.body.textContent;
    // 处理数据 。。。

    //删除iframe
    setTimeout(function(){
        var _frame = document.getElementById(frameName);
        _frame.parentNode.removeChild(_frame);
    }, 100);
}

iframe的实现大致如此,但是如果文件上传的地址与当前页面不在同一个域下就会出现跨域问题。导致iframe的onload回调里的访问服务返回的数据失败。

这时我们再祭出JSONP这把利剑,来解决跨域问题。首先在上传之前注册一个全局的函数,把函数名发给服务器。服务器需要配合在response里让浏览器直接调用这个函数

// 生成全局函数名,避免冲突
var CALLBACK_NAME = 'CALLBACK_NAME';
var genCallbackName = (function () {
    var i = 0;
    return function () {
        return CALLBACK_NAME + ++i;
    };
})();

var curCallbackName = genCallbackName();
window[curCallbackName] = function(res) {
    // 处理response 。。。

    // 删除iframe
    var _frame = document.getElementById(frameName);
    _frame.parentNode.removeChild(_frame);
    // 删除全局函数本身
    window[curCallbackName] = undefined;
}

// 如果已有其他参数,这里需要判断一下,改为拼接 &callback=
form.action = form.action + '?callback=' + curCallbackName;

免责声明:文章转载自《文件异步上传》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇企业邮箱MX解析记录的作用及测试方法Linux基础-配置网络、集群内主机名设定、ssh登入、bash命令、通配符(元字符)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

1、PyCharm的下载、安装

PyCharm 是python编辑器中,比较顺手的一个。而且可以跨平台,在macos和windows下面都可以用。 (1)安装依赖-JDK 因为pycharm(python IDE)是用Java编写的,所以必须要安装JDK才可以运行。 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/...

python实现获取登录验证码图片

  自动化测试登录页面一般都会存在验证码校验问题,大部分童鞋都是直接把验证码校验关闭掉,下面记录的是如何获取验证码图片的方法: 1.首先获取登录页面图片; 2.确定验证码坐标和大小; 3.通过坐标和尺寸在登录页面上截取验证码图片; 4.截取的验证码图片保存为新的文件。 代码实现如下: from selenium import webdriver impor...

一起学CC3200之开发环境简介(2)烧录程序

一起学CC3200之 开发环境简介(2)烧录程序 阿汤哥 序: 能力有限,难免有错,有问题请联系我,请留言或者邮件联系 QQ群交流:482729453   邮件联系hytga@163.com  资料共享链接http://pan.baidu.com/s/1hqiWB56 版本:20160117     一起学CC3200之    - 1 - 开...

.Net 文件名后缀的详细解释

KeyLife富翁笔记 作者:HongYuan标题: .Net 文件名后缀的详细解释 关键字:分类:个人专区密级: 公开 (评分: , 回复: 0, 阅读: 553) »» .sln:解决方案文件,为解决方案资源管理器提供显示管理文件的图形接口所需的信息。 .csproj:项目文件,创建应用程序所需的引用、数据连接、文件夹和文件的信息。...

3. Android程序生成步骤

  主要流程如下图所示:       所需要的工具列表 名称 功能介绍 在操作系统中的路径 aapt Android资源打包工具 ${ANDROID_SDK_HOME}/platform-tools/appt aidl Android接口描述语言转化为.java文件的工具 ${ANDROID_SDK_HOME}/platform-tools...

【IIS】WebApi和Vue混合发布

何为混合发布呢?   混合发布的意思就是在webapi的主文件夹下,放置vue网站目录   让vue和webapi使用同一个端口号进行访问   比如:原来的webapi地址为:https://47.96.66.32:1234/Webapi/         Vue再发布一个地址是:https://47.96.66.32:1235/        混合后Vu...