Hello World

吞风吻雨葬落日 欺山赶海踏雪径

0%

ajax上传进度条

ajax上传进度条

HTML5已经给出了上传时显示进度条的方案。

html5上传文件

XMLHttpRequest对象在html5中有了新的规范扩展( XMLHttpRequest Level 2 ),他包含了一些新的特性:

  • 处理File, Blob 和 FormData对象上传下载时的字节流
  • 上传下载时的处理进度事件
  • 跨域请求
  • 允许匿名请求
  • 允许设置请求超时

html5的进度事件

根据html5的Progress Events定义,进度事件中包含了以下信息

  • 总传输大小
  • 当前的已传输大小
  • 上传文件大小是否已知
    以上信息就可以计算出上传下载的进度。

实现

demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html>
<head>
<title>Upload Files using XMLHttpRequest - Minimal</title>
</head>
<body>
<form id="form1" enctype="multipart/form-data" method="post" action="Upload.aspx">
<div class="row">
<label for="fileToUpload">Select a File to Upload</label><br />
<input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();"/>
</div>
<div id="fileName"></div>
<div id="fileSize"></div>
<div id="fileType"></div>
<div class="row">
<input type="button" onclick="uploadFile()" value="Upload" />
</div>
<div id="progressNumber"></div>
</form>
</body>
</html>

<input type="file">标签的onchange事件绑定了js函数fileSelected() 每次用户通过浏览本地系统上的文件就会触发此事件。fileSelected()函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';

document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}

当有一个<input type="file"/>元素的变量引用,就能获得FileList的对象(html5中的定义),FileList对象包含了File对象的集合,每个File对象包含了如下属性:

  1. name 文件的名称
  2. type 文件的MIME type (全小写)
  3. size文件的大小(byte单位)

有了这些信息就可以在前端做一些文件大小类型的限制判断。

当用户选择文件后,会通过点击上传按钮来上传文件,这里还要注意上传按钮onclick事件绑定了js函数uploadFile() 。uploadFile()函数如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
function uploadFile() {
var xhr = new XMLHttpRequest();
var fd = document.getElementById('form1').getFormData();

/* event listners */
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
/* Be sure to change the url below to the url of your upload server side script */
xhr.open("POST", "UploadMinimal.aspx");
xhr.send(fd);
}

这里利用FormData对象来创建请求数据,使用XMLHttpRequest来发送请求,FormData实例是可以手动创建的

1
2
3
4
var fd = new FormData();
fd.append("author", "Shiv Kumar");
fd.append("name", "Html 5 File API/FormData");
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);

进度事件则是通过XMLHttpRequest的upload属性的addEventListener来设置的。

1
xhr.upload.addEventListener("progress", uploadProgress, false);

进度条事件处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';
}
else {
document.getElementById('progressNumber').innerHTML = 'unable to compute';
}
}

function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}

function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}

function uploadCanceled(evt) {
alert("The upload has been canceled by the user or the browser dropped the connection.");
}

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<!DOCTYPE html>
<html>
<head>
<title>Upload Files using XMLHttpRequest - Minimal</title>

<script type="text/javascript">
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';

document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}

function uploadFile() {
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "UploadMinimal.aspx");
xhr.send(fd);
}

function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';
}
else {
document.getElementById('progressNumber').innerHTML = 'unable to compute';
}
}

function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}

function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}

function uploadCanceled(evt) {
alert("The upload has been canceled by the user or the browser dropped the connection.");
}
</script>
</head>
<body>
<form id="form1" enctype="multipart/form-data" method="post" action="Upload.aspx">
<div class="row">
<label for="fileToUpload">Select a File to Upload</label><br />
<input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();"/>
</div>
<div id="fileName"></div>
<div id="fileSize"></div>
<div id="fileType"></div>
<div class="row">
<input type="button" onclick="uploadFile()" value="Upload" />
</div>
<div id="progressNumber"></div>
</form>
</body>
</html>

下载demo

另外附上下载的demo:
前端页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<input type="text" id="fileDownloadPath" style="width: 1024px ;"/>
<br>

<button id="uploadAsync" onclick="downloadFileS()">异步下载</button>
<button id="uploadSync" onclick="downloadFileA()">同步下载</button>
<input type="text" id="processBar">
<br>
<input type="text" id="tips" readonly>
<script>
function downloadFileS() {
var downloadInput = document.getElementById('fileDownloadPath');

var downloadPath = downloadInput.value;
if(downloadPath == null || downloadPath == ''){
setTips("请填写文件路径");
return ;
}

var xhr = new XMLHttpRequest();
xhr.open('GET', "download?f=" + downloadPath, true);
xhr.onprogress = downloadProgress;
xhr.responseType = "blob";

xhr.onerror = function(e) {
setTips("下载出错");
};
// 发送ajax请求
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log("ready")
var name = xhr.getResponseHeader("Content-disposition");
var filename = name.substring(20,name.length);
filename = filename.replace(/\"/g, "");
var blob = new Blob([xhr.response], {type: 'application/x-msdownload'});
var u = URL.createObjectURL(blob);
var link = document.createElement('a');
link.href = u;
link.download = filename;
link.click();
}
};
xhr.send();
}


function setTips(message) {
var tips = document.getElementById('tips');
tips.value = message;
}

function downloadFileA() {
var downloadInput = document.getElementById('fileDownloadPath');

var downloadPath = downloadInput.value;
if(downloadPath == null || downloadPath == ''){
setTips("请填写文件路径");
return ;
}

window.location.href = "download?f=" + downloadPath;
}

function downloadProgress(evt) {
if (evt.lengthComputable) {
// evt.loaded:文件上传的大小 evt.total:文件总的大小
var percentComplete = Math.round((evt.loaded) * 100 / evt.total);
// 加载进度条,同时显示信息
setTips(name + " 下载进度:" + percentComplete +" %");
}
}

</script>

java服务端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
@GetMapping("/download")
@ResponseBody
public String download(@RequestParam(value = "f")String f,HttpServletResponse response,
HttpServletRequest request){

if(!f.startsWith("/")){
f = "/" + f;
}

String filePath = fileService.getRoot() + f;

File destFile = new File(filePath);
if(!destFile.exists() || !destFile.isFile()){
return "error";
}

response.setContentType("application/x-msdownload");
response.setCharacterEncoding("UTF-8");

String name = destFile.getName();
String postfix = name.substring(name.lastIndexOf("."));
if (request.getHeader("User-Agent").toLowerCase().indexOf("safari") >= 0) {
response.setHeader("Content-Disposition", "attachment;filename=\"" + new String(name.getBytes(Charset.forName("UTF-8")), Charset.forName("ISO-8859-1")) +"\"");
} else {
try{
response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(name, "utf-8")
+ "\"; filename*=utf-8''" + URLEncoder.encode(name, "utf-8"));

}catch (Exception e){
}
}

try{
FileInputStream fis = new FileInputStream(destFile);
BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());

byte[] buffer = new byte[2048];
int readlength = 0;
while((readlength = fis.read(buffer)) != -1){
bos.write(buffer,0,readlength);
}
try {
fis.close();
} catch (IOException e) {
}
try {
bos.flush();
bos.close();
} catch (IOException e) {
}
}catch (Exception e){
return "exception: " + e.getMessage();
}

return "success";
}

参考

http://www.matlus.com/html5-file-upload-with-progress/