当前位置: 首页 > 编程日记 > 正文

文件上传的渐进式增强

文件上传是最古老的互联网操作之一。

20多年了,它几乎没变,还是原来的样子:操作麻烦、缺乏交互、用户体验不佳。在这个新技术日新月异的时代,显得非常落伍。

bg2012080901.png

网页开发者们想了很多办法,试图提升文件上传的功能和操作体验,在各种Javascript库的基础上,开发了五花八门的插件。可是,由于不同浏览器之间的差异,缺乏统一接口,这些插件要么用起来很麻烦,要么不能普遍适用。

HTML5提供了一系列新的浏览器API,使得文件上传有可能出现革命性变化。英国程序员Remy Sharp总结了这些新的接口,本文在他的文章基础上,讨论如何采用HTML5的API,对文件上传进行渐进式增强,实现以下功能:

* iframe上传

* ajax上传

* 进度条

* 文件预览

* 拖放上传

为了对这些功能有一个感性认识,你可以先看看Remy Sharp提供的范例。

虽然这些API,还没有得到广泛部署,但它们是未来的潮流。有了它们,代码就可以写得非常优雅简洁,上面五个功能都能在20行以内实现。

bg2012080902.png

一、传统形式

让我们从最基本的开始。

文件上传的传统形式,是使用表单元素file:

<form id="upload-form" action="upload.php" method="post" enctype="multipart/form-data" >

<input type="file" id="upload" name="upload" /> <br />

<input type="submit" value="Upload" />

</form>

所有浏览器都支持上面的代码。它在IE浏览器中,显示如下:

bg2012080903.png

用户先选择文件,然后点击"Upload"按钮,文件开始上传。

二、iframe上传

传统的表单上传,属于"同步上传"。也就是说,点击上传按钮后,网页"锁死",用户只能等待上传结束,然后浏览器刷新,跳到表单的action属性指定的网址。

有没有办法"异步上传",在网页不重载的情况下,完成整个上传过程呢?

在HTML5没有出现之前,只能使用iframe做到这一点。用户点击submit时,动态插入一个iframe元素(以下代码使用了jQuery函数库)。

var form = $("#upload-form");

form.on('submit',function() {

// 此处动态插入iframe元素

});

插入iframe的代码如下:

var seed = Math.floor(Math.random() * 1000);

var id = "uploader-frame-" + seed;

var callback = "uploader-cb-" + seed;

var iframe = $('<iframe id="'+id+'" name="'+id+'" style="display:none;">');

var url = form.attr('action');

form.attr('target', id).append(iframe).attr('action', url + '?iframe=' + callback);

最后一行,有两个地方值得注意。首先,它为表单添加target属性,指向动态插入的iframe窗口,这使得上传结束后,服务器将结果返回iframe窗口,所以当前页面就不会跳转了。其次,它在action属性指定的上传网址的后面,添加了一个参数,使得服务器知道回调函数的名称。这样就能将服务器返回的信息,从iframe窗口传到上层页面。

服务器(upload.php)返回的信息,应该是如下形式:

<script type="text/javascript">

window.top.window['callback'](data);

</script>

然后,在当前网页定义回调函数:

window[callback] = function(data){

console.log('received callback:', data);

iframe.remove(); //removing iframe

form.removeAttr('target');

form.attr('action', url);

window[callback] = undefined; //removing callback

};

三、ajax上传

HTML5提出了XMLHttpRequest对象的第二版,从此ajax能够上传文件了。这是真正的"异步上传",是将来的主流。上一节的iframe上传,可以用作老式浏览器的替代方案。

ajax上传代码,放在表单的submit事件回调函数中:

form.on('submit',function() {

// 此处进行ajax上传

});

我们主要用的是FormData对象,它能够构建类似表单的键值对。

// 检查是否支持FormData
if(window.FormData) {

var formData = new FormData();

// 建立一个upload表单项,值为上传的文件
formData.append('upload', document.getElementById('upload').files[0]);

var xhr = new XMLHttpRequest();

xhr.open('POST', $(this).attr('action'));

// 定义上传完成后的回调函数
xhr.onload = function () {

if (xhr.status === 200) {

console.log('上传成功');

} else {

console.log('出错了');

}

};

xhr.send(formData);

}

四、进度条

XMLHttpRequest第二版还定义了一个progress事件,可以用来制作进度条。

首先,在页面中放置一个HTML元素progress。

<progress id="uploadprogress" min="0" max="100" value="0">0</progress>

然后,定义progress事件的回调函数。

xhr.upload.onprogress = function (event) {

if (event.lengthComputable) {

var complete = (event.loaded / event.total * 100 | 0);

var progress = document.getElementById('uploadprogress');

progress.value = progress.innerHTML = complete;

}

};

注意,progress事件不是定义在xhr,而是定义在xhr.upload,因为这里需要区分下载和上传,下载也有一个progress事件。

五、图片预览

如果上传的是图片文件,利用File API,我们可以做一个图片文件的预览。这里主要用到FileReader对象。

// 检查是否支持FileReader对象
if (typeof FileReader != 'undefined') {

var acceptedTypes = {
'image/png': true,
'image/jpeg': true,
'image/gif': true
};

if (acceptedTypes[document.getElementById('upload').files[0].type] === true) {

var reader = new FileReader();

reader.onload = function (event) {

var image = new Image();

image.src = event.target.result;

image.width = 100;

document.body.appendChild(image);

};

reader.readAsDataURL(document.getElementById('upload').files[0]);

}

}

六、拖放上传

最后,利用HTML5的拖放功能,实现拖放上传。

先在页面中放置一个容器,用来接收拖放的文件。

<div id="holder"></div>

对它设置样式:

#holder {
border: 10px dashed #ccc;
width: 300px;
min-height: 300px;
margin: 20px auto;
}

#holder.hover {
border: 10px dashed #0c0;
}

拖放文件的代码,主要是定义dragover、dragend和drop这三个事件。

// 检查浏览器是否支持拖放上传。
if('draggable' in document.createElement('span')){

var holder = document.getElementById('holder');

holder.ondragover = function () { this.className = 'hover'; return false; };

holder.ondragend = function () { this.className = ''; return false; };

holder.ondrop = function (event) {

event.preventDefault();

this.className = '';

var files = event.dataTransfer.files;

// do something with files

};

}

完成后的效果和总体代码,请看拖放上传demo。


相关文章:

使用 mkdocs 搭建个人 wiki 站点

原文&#xff1a; https://rebootcat.com/2020/09/20/wiki/ why wiki 博客通常是用来记录一些完整的文章&#xff0c;每篇文章有一个主题。但是我想把平日里的一些笔记也记录到我的博客里&#xff0c;但笔记是零散的&#xff0c;随时的&#xff0c;不是完整的一个主题。所以打…

追忆童年,教你用Python画出儿时卡通人物

作者 | 张同学来源 | 凹凸数据大家好&#xff0c;我是张同学。又到一年一度的国际儿童节&#xff0c;作为逢节必过的程序猿&#xff0c;怎么可以放过这个学习技能的机会呢&#xff1f;于是&#xff0c;今天我们来学习 Python 的 Turtle 库绘制童年的卡通人物&#xff0c;一起做…

xx学OD -- 消息断点 RUN跟踪(上)

这一篇讲的是 消息断点和RUN跟踪的简单知识这一篇没怎么看明白 大概使用知道了 怎么用不太清楚。介绍本次软件特点输入后木有反应(纱布垃圾的。。哈哈)启动OllyDBG载入这个程序&#xff0c;F9让它运行。这个程序按我们前面讲的采用字串参考或函数参考的方法都很容易断下来。…

iOS-UIWebView添加头部和尾部

2019独角兽企业重金招聘Python工程师标准>>> 前言 项目中使用UIWebView显示本地或者是服务器上的页面很容易&#xff0c;但是仅限于显示页面&#xff0c;可控性并不高&#xff0c;如果有需要在头部和尾部加上原生的view&#xff0c;显示部分信息相对于全部使用UIWeb…

epoll 入门例子 tcp server/client

原文&#xff1a; https://rebootcat.com/2020/09/26/epoll_examples/ 复习一下 上一篇博文 epoll原理深入分析 详细分析了 epoll 底层的实现原理&#xff0c;如果对 epoll 原理有模糊的建议先看一下这篇文章。那么本文就开始用 epoll 实现一个简单的 tcp server/client。 本…

张红忠:智慧化时代,如何通吃5G模组、AIoT?

受访者 | 张红忠&#xff0c;日海智能副总裁、云AIoT创新中心总经理记者 | Aholiab&#xff0c;编辑 | 邓晓娟出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;日海智能是目前国内AIoT领域的头部企业&#xff0c;以AIoT为核心&#xff0c;形成了5G&AI物联网终端、…

JQUERY打造隐藏在左侧的弹性弹出菜单

代码简介&#xff1a; 隐藏在左侧的弹性弹出菜单&#xff0c;从淘宝扣下来的&#xff0c;也可作为JAvaScript缓冲动画的典型教程。本弹性菜单可扩展性强&#xff0c;实际上不光可以做成菜单&#xff0c;也可布局一些图文混排的内容或一段视频&#xff0c;总之被弹出的内容是在一…

Num55 boss09(activiti安装以及API)

2019独角兽企业重金招聘Python工程师标准>>> 安装流程设计器插件&#xff1a; 使用框架提供的方式创建23张表&#xff1a; 流程部署&#xff0c;定义&#xff0c;启动&查询&#xff0c;办理任务&#xff1a; 流程的部署&#xff0c;查询&#xff0c;删除 & …

模仿nginx修改进程名

个人博客原文&#xff1a; https://rebootcat.com/2020/10/25/setproctitle/ nginx 进程名 使用 nginx 的过程中&#xff0c;我们经常看到 nginx 的进程名是不同的&#xff0c;如下&#xff1a; $ ps -ef |grep nginx smaug 1183 1115 0 05:46 pts/2 00:00:00 grep …

机器学习模型的超参数优化 | 原力计划

作者 | deephub责编 | 王晓曼出品 | CSDN博客头图 | CSDN付费下载自东方IC引言模型优化是机器学习算法实现中最困难的挑战之一。机器学习和深度学习理论的所有分支都致力于模型的优化。机器学习中的超参数优化旨在寻找使得机器学习算法在验证数据集上表现性能最佳的超参数。超参…

准备IE--复习NP,NP从头开始,每天学一点,就多一点收获

2011-07-07 今天开始重新开始NP&#xff01;每天一实验&#xff0c;加油&#xff01;写下心路历程&#xff0c;为自己加油&#xff01;转载于:https://blog.51cto.com/sunchangsheng/605482

[20160608]自治事务引起死锁.txt

[20160608]自治事务引起死锁.txt --链接http://www.itpub.net/thread-2060966-2-1.html上的讨论,很久以前也遇到过 --(http://blog.itpub.net/267265/viewspace-721262/),时间有点久远,自己重复测试看看: 1.环境: SCOTTtest> &r/ver1 PORT_STRING V…

动感效果的TAB选项卡 jquery 插件

动感效果的TAB选项卡 jquery 插件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns"http://www.w3.org/1999/xhtml"><head><me…

Linux上隐藏进程名(初级版)

缘起 上一篇博文 模仿nginx修改进程名 中提到了一种修改进程名的方法&#xff0c;就像 nginx 一样&#xff0c;给不同进程命名为 master 以及 worker 等。那么能不能把新进程名设置为空字符串呢&#xff1f;如果能&#xff0c;又会有哪些应用场景呢&#xff1f; 答案可能是能…

神操作!一行Python代码搞定一款游戏?给力!

来源&#xff1a;pypl编程榜一直以来Python长期霸占编程语言排行榜前三位&#xff0c;其简洁&#xff0c;功能强大的特性使越来越多的小伙伴开始学习Python 。甚至K12的同学都开始学习Python 编程。新手入门的时候趣味性其实最重要的。那么一行Python 代码到底能玩出什么花样&a…

jquery对所有input type=text的控件赋值

function resetData() { $("input[typetext]").each( function() { $(this).attr("value",""); } ); }

free not return memory

个人博客&#xff1a;https://rebootcat.com/2020/11/05/free_mem/ 内存泄露&#xff1f; 观察到一台机器上的内存使用量在程序启动之后&#xff0c;持续增长&#xff0c;中间没有出现内存恢复。怀疑是不是出现了内存泄露的问题&#xff1f; 然后使用相关的内存分析工具进行了…

成大事必备9种能力

挑战生存的能力&#xff1a;善于在现实中寻找答案 1、摆正心态&#xff0c;敢于面对现实 对于那些不停地抱怨现实恶劣的人来说&#xff0c;不能称心如意的现实&#xff0c;就如同生活的牢笼&a…

懂语言者得天下:NLP凭什么被称为人工智能的掌上明珠?

受访者 | 简仁贤&#xff0c;竹间智能创始人&CEO记者 | 邓晓娟出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;随着技术的发展&#xff0c;大数据、云计算、人工智能、区块链都慢慢地为人熟知。2016 年 Google 推出 AlphaGo&#xff0c;让人工智能走进了大众的视…

[转]SIFT特征提取分析

SIFT&#xff08;Scale-invariant feature transform&#xff09;是一种检测局部特征的算法&#xff0c;该算法通过求一幅图中的特征点&#xff08;interest points,or corner points&#xff09;及其有关scale 和 orientation 的描述子得到特征并进行图像特征点匹配&#xff0…

博客大事记之迁移博客到香港主机

个人博客&#xff1a;https://rebootcat.com/2020/11/10/move_blog_hk/ 前言 之前其实已经写过一篇博文&#xff1a; 迁移博客到香港虚拟空间&#xff0c;那为什么又要写这篇博客呢&#xff1f; 上次其实是把我的博客迁移到一个香港的虚拟空间里&#xff0c;但是不到半年的时…

限时福利:腾讯高级专家手把手教你打造 OCR 神器!

OCR&#xff0c;英文全称即 optical characters recognition&#xff08;光学字符识别&#xff09;&#xff0c;通过服务器把图片上的文字识别出来&#xff0c;以供大家编辑使用&#xff0c;比如进出火车站的时候&#xff0c;已经可以自动识别的身份证&#xff1b;在道路行驶中…

C++与.net的编译方式

C和.Net程序采用了两种不同的编译方式。 通常一个C编写的程序&#xff0c;都是一次编译成二进制的代码&#xff0c;在相应的操作系统平台上直接执行即可。 而.Net程序采用两次编译的方式&#xff0c;用C#&#xff0c;VB.Net等语言写成的程序被编译成IL代码&#xff0c;通过CLR在…

awk (一)

示例文件&#xff1a;[rootorclsrv ~]# catsample Heigh-ho! sing,heigh-ho! unto the green holly: Most friendship isfeigning, most loving mere folly: Then, heigh-ho, theholly!使用感叹号(!) 作为字段分隔符(FS)打印示例数据的第1 个字段&#xff1a;[rootorclsrv~]# …

TCP全连接和半连接的问题探讨

个人博客&#xff1a; https://rebootcat.com/2020/11/14/tcp_accept/ 从何说起 说起 tcp 的连接过程&#xff0c;想必 “3次握手4次挥手”是大家广为熟知的知识&#xff0c;那么关于更细节更底层的连接过程也许就很少人能讲清楚了。 所以本文会先简单回顾一下 tcp 的 3次握手…

[转] ASP.NET MVC3 路由和多数据集的返回

1.ASP.NET MVC3 中的路由 同前边一样本篇并不会过多的介绍理论知识&#xff0c;我们在Global.asax.cs文件中可以看到如下代码&#xff1a; routes.MapRoute("Default", // Route name"{controller}/{action}/{id}", // URL with parametersnew { controlle…

给Python代码加上酷炫进度条的几种姿势

作者 | 刘早起来源 | 早起Python&#xff08;ID: zaoqi-python&#xff09;大家好&#xff0c;在下载某些文件的时候你一定会不时盯着进度条&#xff0c;在写代码的时候使用进度条可以便捷的观察任务处理情况&#xff0c;除了使用print来打印之外&#xff0c;今天本文就介绍几种…

(转)mongodb分片

本文转载自&#xff1a;http://www.cnblogs.com/huangxincheng/archive/2012/03/07/2383284.html 在mongodb里面存在另一种集群&#xff0c;就是分片技术&#xff0c;跟sql server的表分区类似&#xff0c;我们知道当数据量达到T级别的时候&#xff0c;我们的磁盘&#xff0c;内…

深入浅出paxos

原文 https://rebootcat.com/2020/12/05/paxos/

Uber 前无人驾驶工程师告诉你,国内无人驾驶之路还要走多久?

受访者 | Graviti 创始人&CEO 崔运凯记者 | Aholiab&#xff0c;编辑 | Carol出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;经过数年的发展&#xff0c;现在的人们谈到“AI”已经不再像过去一般感到遥不可及。但 AI 在国内发挥的作用仍然只是冰山一角&#xff…