上传文件
1、Form表单上传
接下来我们使用HTML标签来创建文件上传表单,以下为要注意的点:
- form表单 method 属性必须设置为 POST 方法 ,不能使用 GET 方法。
- form表单 enctype 属性需要设置为 multipart/form-data。
enctype 属性规定在发送到服务器之前应该如何对表单数据进行编码。默认地,表单数据会编码为 “application/x-www-form-urlencoded”。就是说,在发送到服务器之前,所有字符都会进行编码(空格转换为 “+” 加号,特殊符号转换为 ASCII HEX 值)。而当设置了该编码格式时,不能直接上传文件。因此,这里我们使用另外一种编码格式,即multipart/form-data,该编码格式不对数据进行编码,而是直接上传二进制数据,form里面的input的值以二进制的方式传过去。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>上传文件</title>
</head>
<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<input name="fff" id="my_file" type="file" />
<input type="submit" value="提交" />
</form>
</body>
</html>
index.html
Python
注意:这种传统的表单上传,属于"同步上传"。也就是说,点击上传按钮后,网页"锁死",用户只能等待上传结束,然后浏览器刷新,跳到表单的action属性指定的网址。
2、AJAX上传
对于传统的表单上传,如果我们上传的是一份大文件的话,那你就在那里干等着吧。如此low的用户体验肯定不是我们想要的。那我们该怎么办呢?这时候我们就要用到Ajax“异步上传”了
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
//创建一个表单对象
function UploadFile(){
var fileObj = document.getElementById("img").files[0];
//主要用的是FormData对象,它能够构建类似表单的键值对。FormData可以凭空创建一个对象,然后往这个对象里面添加数据,然后直接提交
var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj);
var xhr = new XMLHttpRequest();
xhr.open("post", '/index', true);
xhr.send(form);
}
</script>
</body>
</html>
HTML - XMLHttpRequest
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<input type="file" id="img" />
<input type="button" onclick="UploadFile();" />
<script>
function UploadFile(){
var fileObj = $("#img")[0].files[0];
var form = new FormData();
form.append("k1", "v1");
form.append("fff", fileObj);
$.ajax({
type:'POST',
url: '/index',
data: form,
cache: false
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success: function(arg){
console.log(arg);
}
})
}
/*cache
cache设为false可以禁止浏览器对该URL(以及对应的HTTP方法)的缓存。 jQuery通过为URL添加一个冗余参数来实现。
该方法只对GET和HEAD起作用,然而IE8会缓存之前的GET结果来响应POST请求。 这里设置cache: false是为了兼容IE8。
contentType
jQuery中content-type默认值为application/x-www-form-urlencoded, 因此传给data参数的对象会默认被转换为query string(见HTTP 表单编码 enctype)。
我们不需要jQuery做这个转换,否则会破坏掉multipart/form-data的编码格式。 因此设置contentType: false来禁止jQuery的转换操作。
processData
jQuery会将data对象转换为字符串来发送HTTP请求,默认情况下会用 application/x-www-form-urlencoded编码来进行转换。 我们设置contentType: false后该转换会失败,因此设置processData: false来禁止该转换过程。
我们给的data就是已经用FormData编码好的数据,不需要jQuery进行字符串转换。
*/
</script>
</body>
</html>
HTML - jQuery
jQuery文件上传方式依赖于FormData对象, 这是XMLHttpRequest Level 2接口, 需要 IE 10+, Firefox 4.0+, Chrome 7+, Safari 5+, Opera 12+
这意味着对于低版本浏览器只能使用直接提交文件表单的形式, 但提交大文件表单页面会长时间不响应,如果希望在低版本浏览器中解决该问题, 就只能使用别的方式来实现了,比如很多支持多文件和上传进度的Flash插件。
iframe上传
在HTML5没有出现之前,只能用iframe来实现“异步上传”。用户点击submit时,动态插入一个iframe元素
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form id="my_form" name="form" action="/index" method="POST" enctype="multipart/form-data" >
<div id="main">
<input name="fff" id="my_file" type="file" />
<input type="button" name="action" value="Upload" onclick="redirect()"/>
<iframe id='my_iframe' name='my_iframe' src="" class="hide"></iframe>
</div>
</form>
<script>
function redirect(){
document.getElementById('my_iframe').onload = Testt; //iframe加载完成后立马执行
document.getElementById('my_form').target = 'my_iframe'; //,它为表单添加target属性,指向动态插入的iframe窗口,这使得上传结束后,服务器将结果返回iframe窗口,所以当前页面就不会跳转了
document.getElementById('my_form').submit();
}
function Testt(ths){
var t = $("#my_iframe").contents().find("body").text();
console.log(t);
}
</script>
</body>
</html>
HTML - iframe
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.render('index.html')
def post(self, *args, **kwargs):
file_metas = self.request.files["fff"]
# print(file_metas)
for meta in file_metas:
file_name = meta['filename']
with open(file_name,'wb') as up:
up.write(meta['body'])
settings = {
'template_path': 'template',
}
application = tornado.web.Application([
(r"/index", MainHandler),
], **settings)
if __name__ == "__main__":
application.listen(8000)
tornado.ioloop.IOLoop.instance().start()
Python
<script type="text/javascript">
$(document).ready(function () {
$("#formsubmit").click(function () {
var iframe = $('<iframe name="postiframe" id="postiframe" ></iframe>');
$("body").append(iframe);
var form = $('#theuploadform');
form.attr("action", "/upload.aspx");
form.attr("method", "post");
form.attr("encoding", "multipart/form-data");
form.attr("enctype", "multipart/form-data");
form.attr("target", "postiframe");
form.attr("file", $('#userfile').val());
form.submit();
$("#postiframe").load(function () {
iframeContents = this.contentWindow.document.body.innerHTML;
$("#textarea").html(iframeContents);
});
return false;
});
});
</script>
<form id="theuploadform">
<input id="userfile" name="userfile" size="50" type="file" />
<input id="formsubmit" type="submit" value="Send File" />
</form>
<div id="textarea">
</div>
扩展:基于iframe实现Ajax上传示例
$('#upload_iframe').load(function(){
var iframeContents = this.contentWindow.document.body.innerText;
iframeContents = JSON.parse(iframeContents);
})
View Code
function bindChangeAvatar1() {
$('#avatarImg').change(function () {
var file_obj = $(this)[0].files[0];
$('#prevViewImg')[0].src = window.URL.createObjectURL(file_obj)
})
}
function bindChangeAvatar2() {
$('#avatarImg').change(function () {
var file_obj = $(this)[0].files[0];
var reader = new FileReader();
reader.readAsDataURL(file_obj);
reader.onload = function (e) {
$('#previewImg')[0].src = this.result;
};
})
}
function bindChangeAvatar3() {
$('#avatarImg').change(function () {
var file_obj = $(this)[0].files[0];
var form = new FormData();
form.add('img_upload', file_obj);
$.ajax({
url: '',
data: form,
processData: false, // tell jQuery not to process the data
contentType: false, // tell jQuery not to set contentType
success: function (arg) {
}
})
})
}
function bindChangeAvatar4() {
$('#avatarImg').change(function () {
$(this).parent().submit();
$('#upload_iframe').load(function () {
var iframeContents = this.contentWindow.document.body.innerText;
iframeContents = JSON.parse(iframeContents);
if (iframeContents.status) {
$('#previewImg').attr('src', '/' + iframeContents.data);
}
})
})
}
其他
3、进度条
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;
}
};
View Code
注意,progress事件不是定义在xhr,而是定义在xhr.upload,因为这里需要区分下载和上传,下载也有一个progress事件。
4、图片预览
如果上传的是图片文件,利用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]);
}
}
View Code
5、拖放上传
最后,利用HTML5的拖放功能,实现拖放上传。
先在页面中放置一个容器,用来接收拖放的文件。
<div id="holder"></div>
对它设置样式:
#holder {
border: 10px dashed #ccc;
width: 300px;
min-height: 300px;
margin: 20px auto;
}
#holder.hover {
border: 10px dashed #0c0;
}
View Code
拖放文件的代码,主要是定义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
};
}
View Code