本课学习如果上传文件,并把图在浏览器中显示出来。
这个用例在90年代完全可以满足用于IPO的商业模型了,今天我们通过它可以学习两件事情:
1. 如何安装外部node.js模块
2. 如何将它们应用到我们的应用中。
这里,我们要用到的模块是 Felix Geisendorfer开发的 node-formidable模块,它对解析上传的文件数据做了很好的抽象。
如何安装模块呢? node.js有自己的包管理器叫NPM.
看过之前 PhoneGap 之 HelloWorld 的朋友应该会不陌生。
通过下一条命令完成 formidable 的安装
这个用例在90年代完全可以满足用于IPO的商业模型了,今天我们通过它可以学习两件事情:
1. 如何安装外部node.js模块
2. 如何将它们应用到我们的应用中。
这里,我们要用到的模块是 Felix Geisendorfer开发的 node-formidable模块,它对解析上传的文件数据做了很好的抽象。
如何安装模块呢? node.js有自己的包管理器叫NPM.
看过之前 PhoneGap 之 HelloWorld 的朋友应该会不陌生。
通过下一条命令完成 formidable 的安装
npm install formidable
终端输出如下:
npm http GET https://registry.npmjs.org/formidable
npm http 200 https://registry.npmjs.org/formidable
npm http GET https://registry.npmjs.org/formidable/-/formidable-1.0.15.tgz
npm http 200 https://registry.npmjs.org/formidable/-/formidable-1.0.15.tgz
[email protected] node_modules\formidable
说明模块已经安装成功了
查看执行npm install 的目录,会发现多了一个 node_modules/formidable 文件夹
接下来就可以使用它了, 我们先大概了解一下它的用法,一会儿在具体的代码中会把它放在具体的位置。
var formidable = require("formidable");
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files){
// do something to fields & files, for example:
//sys.inspect( {fields:fields, files:files} ); // need require("sys");
});
在处理上传之前,我们得先改一下我们之前的 form.html 给它添加有上传文件的功能,修改后如下:
var formidable = require("formidable");
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files){
// do something to fields & files, for example:
//sys.inspect( {fields:fields, files:files} ); // need require("sys");
});
在处理上传之前,我们得先改一下我们之前的 form.html 给它添加有上传文件的功能,修改后如下:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<textarea name="text" rows="20" cols="60"></textarea>
<input type="file" name="upload" multiple="multiple" />
<input type="submit" value="Submit Text" />
</form>
</body>
</html>
这时候,表格就已经具备上传文件的字段了,如果迫不及待,可以马上重启一下 nodex index 看一下,能够看到选择文件 的组件,随便选择一个文件 (例如:b.py), 并且提交 submit 按钮,可以在cli 里看到文件相关的数据接收日志
received new chunk:------WebKitFormBoundaryr7kREahDZAFtkB0U
Content-Disposition: form-data; name="text"
------WebKitFormBoundaryr7kREahDZAFtkB0U
Content-Disposition: form-data; name="upload"; filename="b.py"
Content-Type: application/octet-stream
from firefly.netconnect.protoc import LiberateFactory
------WebKitFormBoundaryr7kREahDZAFtkB0U--
但是, b.py 上传到哪里了,因为我们没有显示的告诉程序怎么处理收到的文件,所以只知道他上传成功了,在某一临时目录里。具体在哪一个目录下,以及如何把它存到指定的目录以及指定文件名, 这就是接下来我们要学习的了。先修改requestHandler中的upload处理函数, 加入利器formidable:
function upload(response, request){
console.log("request upload");
// parse a file upload
var formidable = require("formidable");
var form = new formidable.IncomingForm();
form.parse(request, function(err, fields, files) {
response.writeHead(200, {'content-type': 'text/plain'});
response.write('received upload:\n\n');
response.end();
console.log("parse done");
console.log(files);
});
}
因为 formidable 的parse函数第一个参数需要的是request, 而我们之前一直传递到requestHanlder的都是postData, 所以需要把原来的postData换成request.
同样的, routes中的postData换成request.
同样的, routes中的postData换成request.
function get(pathname , config, response, request){
console.log("routes to :"+pathname);
if (typeof(config[pathname]) === 'function') {
config[pathname](response, request);
} else{
console.log("routes to empty path: " + pathname);
}
};
server.js 中的postData 也要换成request
注一: setEncodeing不再需要(formidable 自身会处理)
注二:request.addListener("data / end ", function(){}) 不再需要,如果还保留,会造成 formidable.parse不能执行
注一: setEncodeing不再需要(formidable 自身会处理)
注二:request.addListener("data / end ", function(){}) 不再需要,如果还保留,会造成 formidable.parse不能执行
function start(get, config){
var http = require("http");
var url = require("url");
var server = http.createServer(function(request, response){
var pathname = url.parse(request.url).pathname;
get(pathname, config, response, request);
});
server.listen(8888);
}
exports.start = start;
到这里,我们就可以解释之前提过的一个问题了, 上传的文件去哪了?
运行 node index.js 访问 http://localhost:8888/a , 随便选择一个文件,点击Submit
页面上可以看到 成功地跳转到 http://localhost:8888/upload 并显示了 received upload:
后台上可以看到输出 以下输出, 注意绿色标出的就是我们上传文件在服务器的路径, 93f.....6a01就是他临时的新名字,他其它就是我们当时上传的 background.png
运行 node index.js 访问 http://localhost:8888/a , 随便选择一个文件,点击Submit
页面上可以看到 成功地跳转到 http://localhost:8888/upload 并显示了 received upload:
后台上可以看到输出 以下输出, 注意绿色标出的就是我们上传文件在服务器的路径, 93f.....6a01就是他临时的新名字,他其它就是我们当时上传的 background.png
parse done
{ upload:
{ domain: null,
_events: {},
_maxListeners: 10,
size: 4426,
path: 'C:\\Users\\hp\\AppData\\Local\\Temp\\93f36ca422c24e2fd3bb6f9d6d9b6a01',
name: 'background.png',
type: 'image/png',
hash: null,
lastModifiedDate: Thu Jun 19 2014 14:33:14 GMT+0800 (China Standard Time),
_writeStream:
{ _writableState: [Object],
writable: true,
domain: null,
_events: {},
_maxListeners: 10,
path: 'C:\\Users\\hp\\AppData\\Local\\Temp\\93f36ca422c24e2fd3bb6f9d6d9b6a01',
fd: null,
flags: 'w',
mode: 438,
start: undefined,
pos: undefined,
bytesWritten: 4426,
closed: true } } }
接下来, 我们要把解救他,让93f3.....6a01变得有家 (指定路径), 有名号(指定名字). 修改 upload()函数
function upload(response, request){
console.log("request upload");
// parse a file upload
var formidable = require("formidable");
var form = new formidable.IncomingForm();
form.parse(request, function(err, fields, files) {
response.writeHead(200, {'content-type': 'text/plain'});
response.write('received upload :\n\n');
fs.renameSync(files.upload.path, "D:\\Work\\web\\node\\pics\\bob.png");
response.end();
console.log("parse done");
console.log(files);
});
}
使用renameSync来修改名字是正确的,但是如果运行,会得到下面的错误
fs.js:543
return binding.rename(pathModule._makeLong(oldPath),
^
Error: ENOENT, no such file or directory 'C:\Users\hp\AppData\Local\Temp\6dfd199616ca5c0a237702b623b67eec'
at Object.fs.renameSync (fs.js:543:18)
at D:\Work\web\node\requestHandler.js:46:11
at IncomingForm.<anonymous> (D:\Work\web\node\node_modules\formidable\lib\incoming_form.js:104:9)
at IncomingForm.EventEmitter.emit (events.js:92:17)
at IncomingForm._maybeEnd (D:\Work\web\node\node_modules\formidable\lib\incoming_form.js:551:8)
at D:\Work\web\node\node_modules\formidable\lib\incoming_form.js:229:12
at WriteStream.<anonymous> (D:\Work\web\node\node_modules\formidable\lib\file.js:70:5)
at WriteStream.g (events.js:180:16)
at WriteStream.EventEmitter.emit (events.js:117:20)
at finishMaybe (_stream_writable.js:354:12)
为什么呢? 注意看,文件上传的路径在c:盘下 ,而我们要重命名的路径是d:
而fs.rename 是不运行跨分区来移动重命名的。 所以我们要加上uploadDir来指定路径,路径需要和rename的路径在同一分区。有兴趣的同志可以参考此文 Why upload exception
另外,文件的同步操作最好加上try catch 要不然一旦有错误会导致进程直接退出,
所以修改后的upload函数应该是这样子的:
而fs.rename 是不运行跨分区来移动重命名的。 所以我们要加上uploadDir来指定路径,路径需要和rename的路径在同一分区。有兴趣的同志可以参考此文 Why upload exception
另外,文件的同步操作最好加上try catch 要不然一旦有错误会导致进程直接退出,
所以修改后的upload函数应该是这样子的:
function upload(response, request){
console.log("request upload");
// parse a file upload
var formidable = require("formidable");
var form = new formidable.IncomingForm();
form.uploadDir = "D:\\Work\\web\\node\\pics";
form.parse(request, function(err, fields, files) {
response.writeHead(200, {'content-type': 'text/plain'});
response.write('received upload :\n\n');
try{
fs.renameSync(files.upload.path, "D:\\Work\\web\\node\\pics\\"+files.upload.name);
}catch(e){
console.log(e);
}
response.end();
console.log("parse done");
console.log(files);
});
}
好了,重启服务器 node index ,访问http://localhost:8888/a
随便指定一个图片,点击Submit 可以看到页面跳转到了 http://localhost:8888/upload
再去指定的路径看一下 D:\\Work\\web\\node\\pics\\ 是不是看到刚刚选择上传的图片呢 :)
对formidable感兴趣的可以继续阅读
node-formidable详解
Form 表单数据处理 简单教程 formidable 使用心得
npm 之 formidable 介绍
收工!
随便指定一个图片,点击Submit 可以看到页面跳转到了 http://localhost:8888/upload
再去指定的路径看一下 D:\\Work\\web\\node\\pics\\ 是不是看到刚刚选择上传的图片呢 :)
对formidable感兴趣的可以继续阅读
node-formidable详解
Form 表单数据处理 简单教程 formidable 使用心得
npm 之 formidable 介绍
收工!
lesson-12.zip |