Skip to content

Commit e02b9b0

Browse files
committed
init
1 parent e2e4a5d commit e02b9b0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+87903
-1
lines changed

README.md

+107-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,107 @@
1-
# javascript-file-upload
1+
# javascript文件上传
2+
3+
## 1、原生的上传文件,使用form实现
4+
5+
首先,来看第一个例子。
6+
它是一个原生的文件提交方法,前端只有一段html而没有js。我们的目的是观察http协议的格式。
7+
8+
前端index.html,使用一个input标签进行文件选择,然后使用form表单发送数据。
9+
后端server.js(没错~后端程序也由我们编写),对表单发过来的数据进行解析,并用便于观察的方式打印出来。
10+
11+
本程序需要安装node,启动的方式
12+
13+
~~~bash
14+
windows
15+
c:\> node jsupload\demo1\server.js
16+
linux or mac
17+
$ sudo node jsupload/demo1/server.js
18+
~~~
19+
20+
类推,运行demo2的时候
21+
去执行demo2下的server.js就可以了
22+
23+
然后在浏览器中执行
24+
http://localhost
25+
就可以了。
26+
27+
如果你遇到EADDRINUSE的错误,那是因为80端口已经被其它诸如apache、nginx的程序占用了。
28+
可以在启动的时候指定端口, 比如端口3000
29+
30+
~~~bash
31+
$ node server.js 3000
32+
~~~
33+
34+
TIP: 观察。注意这个词。它是我们本次学习之旅的主要方法。你一定要运行每个例子,亲眼看到它们的结果。
35+
36+
它们发生了、产生结果了,你眼见为实了、反复的确认后,就熟悉了这个技术。
37+
38+
## 2、plupload的原理
39+
plupload是一个文件上传的前端插件。
40+
它的主页
41+
http://www.plupload.com/
42+
github地址
43+
https://github.com/moxiecode/plupload
44+
45+
demo2这个例子呢,用来说明plupload的原理。它并没有使用plupload,而是使用XMLHttpRequest发送文件。
46+
它相当于plupload的v0.01版本。
47+
48+
## 3、moxie文件选取fill-picker,带有预览功能
49+
当你看到moxie的时候,可能会觉得莫名其妙。是这样的
50+
51+
打开http://www.plupload.com/docs/
52+
最重要的一段话如下,我跟你一样,一开始读不懂。
53+
其实我写本文的本来动力,也会为了读懂这四句话。
54+
55+
Low-level pollyfills (mOxie) - have their own code base and documentation on GitHub.
56+
Plupload API
57+
UI Widget
58+
Queue Widget
59+
60+
这四句话的意思是
61+
plupload有四个安装等级 - 初级,中级,高级,长级
62+
63+
初级,叫moxie.min.js,插件大小77k到106k不等(神马鬼?为什么不等的原因参见“编译moxie”一节)
64+
中级,plupload.full.min.js,插件大小123k
65+
打开它看一下,发现它其实是moxie.min.js和一个叫plupload.min.js的文件合并到一起而已。
66+
所以plupload其实是在moxie的基础上,封装了一下
67+
高级,它依赖
68+
jquery 137k
69+
jquery ui 282k
70+
plupload 123k
71+
plupload ui 30k
72+
一共约600k的大小
73+
74+
那么回过头,来看这个例子。这个例子只是演示文件选择,它没有上传的功能。
75+
只有文件选择功能的moxie插件的大小为77k,比正常功能要小30%。
76+
那么moxie都做了什么呢,为甚么有77k这么大的体积。它解决了浏览器的兼容性问题、文件预览功能、图片压缩功能、国际化支持(就是i18n)
77+
78+
## 4、上一个基础上,增加了文件上传,带有上传百分比通知
79+
80+
## 5、使用plupload实现了图片上传。
81+
82+
这个例子,比较实际一点,使用plupload。plupload主要在moxie上实现一套事件驱动的机制。
83+
同时,顺带演习上传的暂停和重传。为甚么在这里演习暂停和重传呢?因为为了区分下面的断点续传。
84+
重传是说,不重启浏览器的前提下,重新传文件。实际场景中,可以用来解决4g网不稳定而导致的重试。
85+
但是如果重启了浏览器或者电脑,睡个觉或者去趟三亚回来后,怎么断点续传。
86+
没错,这次我传的是一个电影,已经传了一半了,你让我重传?
87+
下一个例子演示断点续传。
88+
89+
# 6、断点续传
90+
91+
这次服务器的启动时,需要一个“百分比”的参数
92+
$ node server.js 50%
93+
如果同时你还要指定端口号,那么
94+
$ node server.js 3000 50%
95+
96+
你可能会误认为服务器会从50%的地方把数据存起来,不是的,
97+
它的意思是告诉客户端,“请从50%的地方把剩下的文件数据发送过来“。
98+
99+
客户端在发送数据前,询问服务器,上次传送的百分比。然后从这个百分比处发送剩下的数据。
100+
101+
## 7、plupload ui widget的示例
102+
103+
这个例子,用来展示plupload ui widget是个什么东西。
104+
除非你看到它,否则我怎么描述都没用。
105+
如果你看到它了,没错!就是它!
106+
107+

demo1/index.html

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>demo 1</title>
5+
</head>
6+
<body>
7+
<form action="/upload" enctype="multipart/form-data" method="post">
8+
<input type="file" name="upload" /><br>
9+
<input type="submit" value="Upload" />
10+
</form>
11+
</body>
12+
</html>

demo1/server.js

+141
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
$ node server.js
3+
or
4+
$ node server.js 1234
5+
|
6+
V
7+
port
8+
*/
9+
var http = require("http"),
10+
url = require("url"),
11+
path = require("path"),
12+
fs = require("fs");
13+
14+
var port = process.argv[2] || 80;
15+
16+
function formidable(req, res) {
17+
// parse a file upload
18+
var expect = 'sp';
19+
var sp, cont, type, total = 0;
20+
req.on('data', function(tr) {
21+
//console.log('trunk:', tr, tr.toString());
22+
while(1) {
23+
switch(expect) {
24+
case 'sp':
25+
var idx = tr.indexOf('\r\n');
26+
if(idx == -1) return;
27+
sp = tr.slice(0, idx).toString();
28+
tr = tr.slice(idx+2);
29+
console.log(sp);
30+
expect = 'content';
31+
break;
32+
case 'content':
33+
var idx = tr.indexOf('\r\n');
34+
cont = tr.slice(0, idx).toString();
35+
console.log(cont);
36+
if(/Content-Disposition: ?form-data;.*filename="/.test(cont)) {
37+
expect = 'type';
38+
tr = tr.slice(idx+2);
39+
} else {
40+
expect = 'value';
41+
tr = tr.slice(idx+4);
42+
}
43+
break;
44+
case 'value':
45+
var idx = tr.indexOf('\r\n');
46+
value = tr.slice(0, idx).toString();
47+
tr = tr.slice(idx+2);
48+
console.log(value);
49+
expect = 'sp';
50+
break;
51+
case 'type':
52+
var idx = tr.indexOf('\r\n');
53+
type = tr.slice(0, idx).toString();
54+
tr = tr.slice(idx+4);
55+
console.log('type:', type);
56+
expect = 'end';
57+
break;
58+
case 'end':
59+
var idx = tr.indexOf('\r\n'+sp);
60+
process.stdout.write('.');
61+
if(idx >= 0) {
62+
total += idx;
63+
} else total += tr.length;
64+
return;
65+
}
66+
}
67+
}).on('end',function() {
68+
console.log('\ntotal:', total);
69+
res.end(`<head>
70+
<meta http-equiv="Content-Type" content="text/html; charset=gbk" />
71+
</head>
72+
<body><p>${cont}</p>
73+
<p>total: ${total}</p>
74+
</body>`);
75+
});
76+
}
77+
78+
var mimeTypes = {
79+
"htm": "text/html",
80+
"html": "text/html",
81+
"jpeg": "image/jpeg",
82+
"jpg": "image/jpeg",
83+
"png": "image/png",
84+
"gif": "image/gif",
85+
"js": "text/javascript",
86+
"css": "text/css"};
87+
88+
var virtualDirectories = {
89+
//"images": "../images/"
90+
};
91+
92+
process.chdir(__dirname);
93+
94+
http.createServer(function(request, response) {
95+
if (request.url == '/upload' && request.method.toLowerCase() == 'post') {
96+
console.log('post', request.url);
97+
formidable(request, response);
98+
return;
99+
}
100+
101+
var uri = url.parse(request.url).pathname
102+
, filename = path.join(process.cwd(), uri)
103+
, root = uri.split("/")[1]
104+
, virtualDirectory;
105+
106+
virtualDirectory = virtualDirectories[root];
107+
if(virtualDirectory){
108+
uri = uri.slice(root.length + 1, uri.length);
109+
filename = path.join(virtualDirectory ,uri);
110+
}
111+
112+
fs.exists(filename, function(exists) {
113+
if(!exists) {
114+
response.writeHead(404, {"Content-Type": "text/plain"});
115+
response.write("404 Not Found\n");
116+
response.end();
117+
console.error('404: ' + filename);
118+
return;
119+
}
120+
121+
if (fs.statSync(filename).isDirectory()) filename += '/index.html';
122+
123+
fs.readFile(filename, "binary", function(err, file) {
124+
if(err) {
125+
response.writeHead(500, {"Content-Type": "text/plain"});
126+
response.write(err + "\n");
127+
response.end();
128+
console.error('500: ' + filename);
129+
return;
130+
}
131+
132+
var mimeType = mimeTypes[path.extname(filename).split(".")[1]];
133+
response.writeHead(200, {"Content-Type": mimeType});
134+
response.write(file, "binary");
135+
response.end();
136+
console.log('200: ' + filename + ' as ' + mimeType);
137+
});
138+
});
139+
}).listen(parseInt(port, 10));
140+
141+
console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown");

demo2/index.html

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>demo 2</title>
5+
</head>
6+
<body>
7+
<input type="file" id="picker" />
8+
<img id="image" />
9+
<script>
10+
window.onload = function() {
11+
document.getElementById("picker").onchange = function () {
12+
var file = this.files[0];
13+
var xhr = new XMLHttpRequest();
14+
xhr.onload = function(d) {
15+
document.getElementById("output")
16+
.innerText = d.currentTarget.response;
17+
};
18+
xhr.onerror = function(err) {
19+
document.getElementById("output")
20+
.innerText = 'error';
21+
};
22+
xhr.open('POST', '/upload');
23+
var fd = new window.FormData();
24+
fd.append('file', file);
25+
xhr.send(fd);
26+
};
27+
};
28+
</script>
29+
<pre id="output"></pre>
30+
</body>
31+
</html>

0 commit comments

Comments
 (0)