Nodejs學習筆記(4) 文件操作 fs 及 express 上傳
目錄
- 參考資料
- 1. fs 模塊
- 1.1 讀取文件fs.readFile
- 1.2 寫入文件fs.writeFile
- 1.3 獲取文件信息fs.stat
- 1.4 刪除文件fs.unlink
- 1.5 清空指定文件夾 fs.unlink + fs.readdir
- 2. 關於 HTTP 文件傳輸和 multer 控制文件上傳的幾個問題(寫在前面)
- 2.1 文件選擇後(未提交前)放在哪裏?
- 2.2 文件提交後的路徑是什麽?
- 2.3 文件傳輸在HTTP協議中是如何進行的?
2.4 multer([options])中有哪些鍵?分別有什麽用??
- 2.5 multer.array()有什麽用??
- 2.6 使用不同瀏覽器傳輸文件會有什麽不同效果??
- 3. HTML 用於上傳文件的元素
- 3.1 HTML < form > 標簽的 enctype 屬性
- 3.2 HTML DOM FileUpload 對象
- 4. express 文件上傳
- 4.1 文件上傳的實例
- 4.2 multer模塊是什麽?
- 4.3 multer如何控制文件傳輸?
- 4.3.1 控制編碼文件的位置
- 4.3.2 給不同的文件響應規定字段名
4.3.3 控制接收文件的類型
- 4.3 設置本地存儲路徑(通過 req 對象的屬性)
- 4.4 緩存管理
參考資料
Node.js 文件系統 | 菜鳥教程
HTML DOM FileUpload 對象 | W3school
HTML <form> 標簽 | W3school
HTML <input> 標簽 | W3school
HTML <input> 標簽的 accept 屬性
multer - npm
multer模塊的使用 +文件上傳+ 評論 | 維克多噗噗的博客
1. fs 模塊
?該模塊主要執行文件操作,操作的方法均有同步和異步版本,例如讀取文件內容的函數有異步的 fs.readFile() 和同步的 fs.readFileSync()。
?異步的方法函數最後一個參數為回調函數,回調函數的第一個參數包含了錯誤信息(error)。其余參數根據不同的方法有所差異。
?比起同步,異步方法性能更高,速度更快,而且沒有阻塞。在此僅記錄我在 express 上傳文件操作時所用到的readFile
方法、writeFile
方法、stat
方法和unlink
方法,對其余方法僅作簡單描述,詳細使用方法和實例參照Node.js文件系統 | 菜鳥教程。
1.1 讀取文件fs.readFile
fs.readFile(filename,[,options], callback(err, data));
回調函數的參數:
err
- 錯誤信息;data
- buffer數據流對象,可用data.toString()
轉換成字符串;
var fs = require(‘fs‘);
// 異步讀取
fs.readFile(‘./input.txt‘, function (err, data) {
if (err) {
return console.error(err);
}
console.log("異步讀取:" + data.toString());
});
1.2 寫入文件fs.writeFile
fs.writeFile(file, data[, options], callback(err))
?writeFile 直接打開文件默認是w
模式,所以如果文件存在,該方法寫入的內容會覆蓋舊的文件內容。
?參數使用說明如下:
file
- 文件名或文件描述符。data
- 要寫入文件的數據,可以是String(字符串)或Buffer(流)對象。options
- 該參數是一個對象,內容如下:- encoding - 編碼,默認值為
utf8
; - mode - 模式(權限),默認值為
0666
(可讀、可寫); - flag - 文件打開行為,默認值為
‘w‘
;
- encoding - 編碼,默認值為
callback
- 回調函數,回調函數只包含錯誤信息參數(err),在寫入失敗時返回。
?常見的打開文件的模式(mode)有以下幾種:
Flag | 描述 |
---|---|
r | 以讀取模式打開文件。如果文件不存在拋出異常。 |
r+ | 以讀寫模式打開文件。如果文件不存在拋出異常。 |
w | 以寫入模式打開文件,如果文件不存在則創建。 |
w+ | 以讀寫模式打開文件,如果文件不存在則創建。 |
1.3 獲取文件信息fs.stat
fs.stat(path, callback(err, stats))
文件的狀態信息包含在回調函數的參數stats中,這是一個fs.Stats對象,其內容如下:
atime: Wed Jul 25 2018 21:11:59 GMT+0800 (GMT+08:00) {}
atimeMs: 1532524319921.0476
birthtime: Wed Jul 25 2018 21:11:59 GMT+0800 (GMT+08:00) {}
birthtimeMs: 1532524319921.0476
blksize: undefined
blocks: undefined
ctime: Wed Jul 25 2018 21:11:59 GMT+0800 (GMT+08:00) {}
ctimeMs: 1532524319922.0476
dev: 6533005
gid: 0
ino: 1407374884234476
mode: 33206
mtime: Wed Jul 25 2018 21:11:59 GMT+0800 (GMT+08:00) {}
?其中有四個時間值得我們關註:
- atime - 訪問時間(access time);
- birthtime - 創建時間;
- ctime - 狀態修改時間(change time),顯示的是文件的權限、擁有者、所屬的組、鏈接數發生改變時的時間;
- mtime - 修改時間(modify time),顯示的是文件內容被修改的最後時間。
?每一個時間都是一個JavaScript Date()對象的實例,因此有些方法是可以通用的,例如獲取日期、月份、年份:
stats.birthtime.getDate()
25
stats.birthtime.getMonth() // js的月份從0開始算
6
stats.birthtime.getFullYear()
2018
1.4 刪除文件fs.unlink
fs.unlink(path, callback(err))
?直接刪除path
對應的文件,若文件不存在會通過err
報錯。
?其他方法
方法 | 作用 |
---|---|
fs.open(path, flags[, mode], callback(err, fd)) | 打開文件 |
fs.read(fd, buffer, offset, length, position, callback) | 讀取文件 |
fs.close(fd, callback) | 關閉文件 |
fs.ftruncate(fd, len, callback) | 截取文件 |
fs.mkdir(path[, mode], callback) | 創建目錄 |
fs.readdir(path, callback) | 讀取目錄 |
fs.rmdir(path, callback) | 刪除目錄 |
1.5 清空指定文件夾 fs.unlink + fs.readdir
fs.readdir(path, callback(err, files))
?files
為 目錄下的文件數組列表。
2. 關於 HTTP 文件傳輸和 multer 控制文件上傳的幾個問題(寫在前面)
2.1 文件選擇後(未提交前)放在哪裏?
?哪都沒放,還在原先的磁盤上,只是根據選擇文件信息填充了HTML DOM FileUpload的屬性。
2.2 文件提交後的路徑是什麽?
?由服務器設定,在express中由multer({dest: ‘‘})
指定。
2.3 文件傳輸在HTTP協議中是如何進行的?
?將文件編碼後存儲在請求體中,且一旦發送請求(包含請求體和請求頭),就向服務器指定接收文件的位置發送一個編碼文件(存放在multer({dest: ‘‘})
指定的路徑中);
?服務器可以根據請求頭的信息,對編碼文件進行操作(解析、讀取等);
?若直接修改編碼文件的後綴名,可以直接獲得原始文件,例如我發送一個png圖片,在服務器收到了一個名稱為7d5931b2f95ce2cb93e647c6d64f5326
的文件,將其後綴名修改為.png,打開,完美還原。
2.4 multer([options])中有哪些鍵?分別有什麽用??
dest
:指定接收編碼文件的路徑;(用的最多)fileFilter
:控制接收的文件類型;(偶爾用用,在控制文件類型時用到)limits
:Limits of the uploaded data;(基本沒用)preservePath
:Keep the full path of files instead of just the base name;(基本沒用)
2.5 multer.array()有什麽用??
?array()的作用是規定接受的一系列文件共有的字段名(類似於將文件分類)。其可以使用app.use()命名為一個全局中間件,但這並不理想,因為在一個腳本文件中可能需要響應不同類型的文件上傳,有圖片、文檔、XML、JSON等。
?所以更理想的方式是在全局先創建一個multer實例:
var upload = multer({ dest: ‘./tmp/‘});
然後在對每個不同的POST請求響應中,將upload.array(‘‘)
作為第二個參數寫入:
// 響應請求
app.post(‘/image_upload‘, upload.array(‘image‘), function (req, res) {
// code...
})
?一般來說,一個app.post()只能響應一個表單元素的提交,因此對提交的不同類型的表單元素數據設置不同的字段名(fieldname),是最理想的選擇。
2.6 使用不同瀏覽器傳輸文件會有什麽不同效果??
?沒有不同效果,都可以成功傳輸文件,並且都能將編碼文件存儲到指定文件夾中。只是請求頭的user-agent
信息會有不同。
3. HTML 用於上傳文件的元素
3.1 HTML < form > 標簽的 enctype 屬性
HTML <form> 標簽 | W3school
HTML <input> 標簽 | W3school
?enctype 屬性規定在發送到服務器之前應該如何對表單數據進行編碼;
?默認地,表單數據會編碼為 application/x-www-form-urlencoded
。就是說,在發送到服務器之前,所有字符都會進行編碼(空格轉換為 "+" 加號,特殊符號轉換為 ASCII HEX 值);
?當我們使用文件上傳功能時,enctype的值必須設置為multipart/form-data
。
語法
<form enctype="value">
屬性值
值 | 描述 |
---|---|
application/x-www-form-urlencoded | 在發送前編碼所有字符(默認) |
multipart/form-data |
不對字符編碼。 在使用包含文件上傳控件的表單時,必須使用該值。 |
text/plain | 空格轉換為 "+" 加號,但不對特殊字符編碼。 |
3.2 HTML DOM FileUpload 對象
HTML DOM FileUpload 對象 | W3school
?在 HTML 文檔中 標簽每出現一次,一個 FileUpload 對象就會被創建。我們可以通過使用document.getElementById()
來訪問 FileUpload 對象:
.html:
<form id="uplodaFile" action="/file_upload" method="POST" enctype="multipart/form-data">
<input type="file" name="image" size="50"><br>
<input type="submit" value="上傳圖片">
</form>
.js(獲取 FileUpload 對象):
var element = document.getElementById("uplodaFile");
FileUpload 對象的屬性:
屬性 | 描述 |
---|---|
accept |
設置或返回指示文件傳輸的 MIME 類型的列表(逗號分隔)。 Content-Type |
accessKey | 設置或返回訪問 FileUpload 對象的快捷鍵。 |
alt | 設置或返回不支持 <input type="file"> 時顯示的替代文字。 |
defaultValue | 設置或返回 FileUpload 對象的初始值。 |
disabled | 設置或返回是否禁用 FileUpload 對象。 |
form | 返回對包含 FileUpload 對象的表單的引用。 |
id | 設置或返回 FileUpload 對象的 id。 |
name | 設置或返回 FileUpload 對象的名稱。 |
tabIndex | 設置或返回定義 FileUpload 對象的 tab 鍵控制次序的索引號。 |
type | 返回表單元素的類型。對於 FileUpload ,則是 "file" 。 |
value | 返回由用戶輸入設置的文本後,FileUpload 對象的文件名。 |
4. express 文件上傳
4.1 文件上傳的實例
upload.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload Page</title>
</head>
<body>
<h2>UPLOAD IMAGE FILE</h2><br>
<form id="uploadImg" action="/image_upload" method="POST" enctype="multipart/form-data" >
<input type="file" name="image" accept="image/*"><br>
<input type="submit" value="上傳圖片">
</form>
<script type="text/javascript">
var element = document.getElementById("uploadImg");
</script>
</body>
</html>
upload.js:
/**
* 上傳圖片文件測試腳本
*/
// 依賴
var express = require(‘express‘);
var app = express();
var fs = require(‘fs‘);
var bodyParser = require(‘body-parser‘);
var multer = require(‘multer‘);
// 中間件
app.use(express.static(‘./uploads/‘));
app.use(bodyParser.urlencoded({ extended: false }));
// 控制允許接收的文件類型(4.3.3)
var fileFilter = function fileFilter (req, file, cd) {
if (file.mimetype == "image/png" || file.mimetype == "image/jpeg"){
cd(null, true);
}else{
req.error = "不允許上傳" + file.mimetype + "類型的文件!";
cd(null, false);
}
}
// 設置路徑和文件過濾器(4.3)
var upload = multer({ dest: ‘./uploadFiles/tmp/‘, fileFilter: fileFilter});
// 首頁
app.get(‘/‘, function (req, res) {
res.sendFile(__dirname + "/" + "upload.html");
})
// 響應請求
app.post(‘/image_upload‘, upload.array(‘image‘), function (req, res) {
// 文件信息
if(!req.files[0]){
console.log(req.error);
res.send(req.error);
return;
}else{
console.log(req.files[0]);
}
// 存儲並響應客戶端
var des_file = __dirname + "/uploadFiles/" + req.files[0].fieldname + "/" + req.files[0].originalname;
fs.readFile(req.files[0].path, function (err, data) {
fs.writeFile(des_file, data, function (err) {
if(err){
console.log(err);
}else{
var response = {
message: ‘File uploaded successfully‘,
filename: req.files[0].originalname
};
console.log(response);
res.json(response);
}
});
});
})
// 監聽
var server = app.listen(3333, function () {
var host = server.address().address;
var port = server.address().port;
console.log("應用實例,訪問地址為:http://%s:%s", host, port);
})
4.2 multer模塊是什麽?
multer - npm
?Multer是一個專門用於處理multipart/form-data
編碼類型數據流的node.js中間件,在進行文件上傳操作時常用到。
?需要註意的是,當表單元素的編碼類型不是multipart/form-data
時,Multer不會對請求進行解析。
?我們一般通過如下方法使用multer模塊:
var multer = require(‘multer‘);
var upload = multer({ dest: ‘./tmp/‘});
// 響應請求
app.post(‘/image_upload‘, upload.array(‘image‘), function (req, res) {
// code...
})
4.3 multer如何控制文件傳輸?
multer - npm
4.3.1 控制編碼文件的位置
multer({ dest: ‘./uploadFiles/tmp/‘ });
4.3.2 給不同的文件響應規定字段名
multer.array(‘image‘);
multer.array(‘myType‘);
4.3.3 控制接收文件的類型
IMME文件類型:Content-Type
前端控制
HTML <input> 標簽的 accept 屬性
?為表單元素<input type="file">
設置屬性accept
,限定文件選擇對話框中允許選擇的文件類型(多種類型用逗號分隔):
<input type="file" name="image" accept="image/png, application/pdf"><br>
服務端控制
multer模塊的使用 +文件上傳+ 評論 | 維克多噗噗的博客
multer(fileFilter) - npm
?在服務端控制接收文件的類型,主要依靠multer([options])
中的fileFilter
鍵(multer的鍵值)。fileFilter
鍵的使用方法是:創建一個函數fileFilter(req, file, cd){}
,來對請求進行解析,進而通過參數cd
決定是否接收發送的文件。
?錯誤的使用:
// 不能直接規定fileFilter的鍵值
var upload = multer({ dest: ‘.upload‘, fileFilter: ‘image/png, image/jpeg‘});
?正確的使用:
// 控制允許接收的文件類型
function fileFilter (req, file, cd) {
if (file.mimetype == "image/png" || file.mimetype == "image/jpeg"){
cd(null, true); // 同意接收文件
}else{
req.error = "不允許上傳" + file.mimetype + "類型的文件!";
cd(null, false); // 拒絕接收文件
}
}
var upload = multer({ dest: ‘./uploadFiles/tmp/‘, fileFilter: fileFilter});
file
包含以下字段encoding
:"7bit";fieldname
:"image";(字段名:由upload.array(‘image‘)
定義的)mimetype
:"image/jpeg";originalname
:"540ff7cddc29e.jpg";
cd
的用法cd(null, true)
- To accept the file passtrue
cd(null, false)
- To reject this file passfalse
4.3 設置本地存儲路徑(通過 req 對象的屬性)
?在文件目錄下創建uploadFiles文件夾,同時根據upload.array()中規定的字段名創建文件夾(一定不能創建出錯,不然會提示無法打開相應的文件夾);
?例如upload.array(‘image‘)
,創建uploadFiles/image,使用下面方法可以將文件存入到image文件夾中:
var des_file = __dirname + "/uploadFiles/" + req.files[0].fieldname + "/" + req.files[0].originalname;
fs.readFile(req.files[0].path, function (err, data) {
fs.writeFile(des_file, data, function (err) {
//callback...
});
});
4.4 緩存管理
?在接收文件時,服務器將受到大量的編碼文件,當完成文件接收後,這些編碼的文件仍然存放在服務器主機磁盤上。這些文件的存在有利於數據的恢復,但當其數量達到一定規模時,會對磁盤空間造成較大的壓力,因此,應該采取合適的手段進行編碼文件的數量控制,來保證磁盤空間的可用性。
// 刪除傳輸文件時的臨時文件
var fs = require(‘fs‘);
var desDir = "D:/nodejs/my-sql/uploadFiles/tmp/";
// 先獲取該文件夾下所有文件名
fs.readdir(desDir, function (err, files) {
if (err) {
return console.error(err);
}
for (var i=0; i<files.length; i++) {
// 使用 unlink 刪除
fs.unlink(desDir + files[i], function (err){
if (err) {
return console.error(err);
}
console.log("Successfully delete file " + files[i].toString()); // 註意應轉換成字符串
})
}
})
Nodejs學習筆記(4) 文件操作 fs 及 express 上傳