Bạn có thể tải tệp lên bằng Meteor mà không cần sử dụng thêm gói hoặc bên thứ ba nào
Tùy chọn 1:DDP, lưu tệp vào bộ sưu tập mongo
/*** client.js ***/
// asign a change event into input tag
'change input' : function(event,template){
var file = event.target.files[0]; //assuming 1 file only
if (!file) return;
var reader = new FileReader(); //create a reader according to HTML5 File API
reader.onload = function(event){
var buffer = new Uint8Array(reader.result) // convert to binary
Meteor.call('saveFile', buffer);
}
reader.readAsArrayBuffer(file); //read the file as arraybuffer
}
/*** server.js ***/
Files = new Mongo.Collection('files');
Meteor.methods({
'saveFile': function(buffer){
Files.insert({data:buffer})
}
});
Giải thích
Đầu tiên, tệp được lấy từ đầu vào bằng API tệp HTML5. Một trình đọc được tạo bằng FileReader mới. Tệp được đọc dưới dạng readAsArrayBuffer. Bộ đệm mảng này, nếu bạn console.log, trả về {} và DDP không thể gửi dữ liệu này qua dây, vì vậy nó phải được chuyển đổi thành Uint8Array.
Khi bạn đặt nó trong Meteor.call, Meteor sẽ tự động chạy EJSON.stringify (Uint8Array) và gửi nó bằng DDP. Bạn có thể kiểm tra dữ liệu trong lưu lượng truy cập websocket của bảng điều khiển chrome, bạn sẽ thấy một chuỗi giống với base64
Ở phía máy chủ, Meteor gọi EJSON.parse () và chuyển nó trở lại bộ đệm
Ưu điểm
- Cách đơn giản, không hack, không có gói bổ sung
- Bám sát nguyên tắc Dữ liệu trên dây
Nhược điểm
- Nhiều băng thông hơn:chuỗi base64 kết quả lớn hơn ~ 33% so với tệp gốc
- Giới hạn kích thước tệp:không thể gửi tệp lớn (giới hạn ~ 16 MB?)
- Không có bộ nhớ đệm
- Chưa có gzip hoặc nén
- Tốn nhiều bộ nhớ nếu bạn xuất bản tệp
Tùy chọn 2:XHR, đăng từ ứng dụng khách lên hệ thống tệp
/*** client.js ***/
// asign a change event into input tag
'change input' : function(event,template){
var file = event.target.files[0];
if (!file) return;
var xhr = new XMLHttpRequest();
xhr.open('POST', '/uploadSomeWhere', true);
xhr.onload = function(event){...}
xhr.send(file);
}
/*** server.js ***/
var fs = Npm.require('fs');
//using interal webapp or iron:router
WebApp.connectHandlers.use('/uploadSomeWhere',function(req,res){
//var start = Date.now()
var file = fs.createWriteStream('/path/to/dir/filename');
file.on('error',function(error){...});
file.on('finish',function(){
res.writeHead(...)
res.end(); //end the respone
//console.log('Finish uploading, time taken: ' + Date.now() - start);
});
req.pipe(file); //pipe the request to the file
});
Giải thích
Tệp trong máy khách được lấy, một đối tượng XHR được tạo và tệp được gửi qua 'POST' đến máy chủ.
Trên máy chủ, dữ liệu được chuyển vào một hệ thống tệp cơ bản. Ngoài ra, bạn có thể xác định tên tệp, thực hiện xóa hoặc kiểm tra xem nó đã tồn tại chưa, v.v. trước khi lưu.
Ưu điểm
- Tận dụng XHR 2 để bạn có thể gửi bộ đệm mảng, không cần FileReader () mới so với tùy chọn 1
- Bộ đệm mảng ít cồng kềnh hơn so với chuỗi base64
- Không có giới hạn kích thước, tôi đã gửi một tệp có dung lượng ~ 200 MB trong máy chủ cục bộ mà không có vấn đề gì
- Hệ thống tệp nhanh hơn mongodb (phần sau của hệ thống này trong phần đo điểm chuẩn bên dưới)
- Có thể truy cập và gzip
Nhược điểm
- XHR 2 không khả dụng trong các trình duyệt cũ hơn, ví dụ:dưới IE10, nhưng tất nhiên bạn có thể triển khai một bài đăng truyền thống
- / path / to / dir / phải ở bên ngoài sao băng, nếu không việc ghi tệp vào / public sẽ kích hoạt tải lại
Tùy chọn 3:XHR, lưu vào GridFS
/*** client.js ***/
//same as option 2
/*** version A: server.js ***/
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var GridStore = MongoInternals.NpmModule.GridStore;
WebApp.connectHandlers.use('/uploadSomeWhere',function(req,res){
//var start = Date.now()
var file = new GridStore(db,'filename','w');
file.open(function(error,gs){
file.stream(true); //true will close the file automatically once piping finishes
file.on('error',function(e){...});
file.on('end',function(){
res.end(); //send end respone
//console.log('Finish uploading, time taken: ' + Date.now() - start);
});
req.pipe(file);
});
});
/*** version B: server.js ***/
var db = MongoInternals.defaultRemoteCollectionDriver().mongo.db;
var GridStore = Npm.require('mongodb').GridStore; //also need to add Npm.depends({mongodb:'2.0.13'}) in package.js
WebApp.connectHandlers.use('/uploadSomeWhere',function(req,res){
//var start = Date.now()
var file = new GridStore(db,'filename','w').stream(true); //start the stream
file.on('error',function(e){...});
file.on('end',function(){
res.end(); //send end respone
//console.log('Finish uploading, time taken: ' + Date.now() - start);
});
req.pipe(file);
});
Giải thích
Tập lệnh máy khách giống như trong tùy chọn 2.
Theo dòng cuối cùng của Meteor 1.0.x mongo_driver.js, một đối tượng toàn cục được gọi là MongoInternals được hiển thị, bạn có thể gọi defaultRemoteCollectionDriver () để trả về đối tượng db cơ sở dữ liệu hiện tại được yêu cầu cho GridStore. Trong phiên bản A, GridStore cũng được giới thiệu bởi MongoInternals. Mongo được sử dụng bởi sao băng hiện tại là v1.4.x
Sau đó, bên trong một tuyến, bạn có thể tạo một đối tượng ghi mới bằng cách gọi var file =new GridStore (...) (API). Sau đó, bạn mở tệp và tạo một luồng.
Tôi cũng bao gồm một phiên bản B. Trong phiên bản này, GridStore được gọi bằng cách sử dụng ổ mongodb mới thông qua Npm.require ('mongodb'), mongo này là v2.0.13 mới nhất tính đến thời điểm viết bài này. API mới không yêu cầu bạn mở tệp, bạn có thể gọi trực tiếp luồng (true) và bắt đầu tạo đường dẫn
Ưu điểm
- Tương tự như trong tùy chọn 2, được gửi bằng bộ đệm mảng, ít chi phí hơn so với chuỗi base64 trong tùy chọn 1
- Không cần phải lo lắng về việc xóa tên tệp
- Tách khỏi hệ thống tệp, không cần ghi vào dir tạm thời, db có thể được sao lưu, đại diện, phân đoạn, v.v.
- Không cần triển khai bất kỳ gói nào khác
- Có thể lưu trữ và có thể được giải nén
- Lưu trữ các kích thước lớn hơn nhiều so với bộ sưu tập mongo thông thường
- Sử dụng đường dẫn để giảm quá tải bộ nhớ
Nhược điểm
- Mongo GridFS không ổn định . Tôi đã bao gồm phiên bản A (mongo 1.x) và B (mongo 2.x). Ở phiên bản A, khi piping tệp lớn> 10 MB, tôi gặp rất nhiều lỗi, bao gồm tệp bị hỏng, đường dẫn chưa hoàn thành. Sự cố này đã được giải quyết trong phiên bản B bằng mongo 2.x, hy vọng sao băng sẽ sớm nâng cấp lên mongodb 2.x
- Sự nhầm lẫn về API . Trong phiên bản A, bạn cần mở tệp trước khi có thể phát trực tiếp, nhưng trong phiên bản B, bạn có thể phát trực tiếp mà không cần gọi mở. Tài liệu API cũng không rõ ràng lắm và luồng không thể trao đổi cú pháp 100% với Npm.require ('fs'). Trong fs, bạn gọi file.on ('finish') nhưng trong GridFS, bạn gọi file.on ('end') khi viết kết thúc / kết thúc.
- GridFS không cung cấp tính nguyên tử khi ghi, vì vậy nếu có nhiều lần ghi đồng thời vào cùng một tệp, kết quả cuối cùng có thể rất khác nhau
- Tốc độ . Mongo GridFS chậm hơn nhiều so với hệ thống tệp.
Điểm chuẩn Bạn có thể thấy trong tùy chọn 2 và tùy chọn 3, tôi đã bao gồm var start =Date.now () và khi viết end, tôi console.log out thời gian bằng mili giây , dưới đây là kết quả. Lõi kép, ram 4 GB, ổ cứng HDD, dựa trên ubuntu 14.04.
Kích thước tệpfile size GridFS FS
100 KB 50 2
1 MB 400 30
10 MB 3500 100
200 MB 80000 1240
Bạn có thể thấy rằng FS nhanh hơn nhiều so với GridFS. Đối với tệp 200 MB, mất ~ 80 giây bằng GridFS nhưng chỉ ~ 1 giây trong FS. Tôi chưa thử SSD, kết quả có thể khác. Tuy nhiên, trong cuộc sống thực, băng thông có thể quyết định tốc độ truyền tệp từ máy khách đến máy chủ, việc đạt được tốc độ truyền 200 MB / giây không phải là điển hình. Mặt khác, tốc độ truyền ~ 2 MB / giây (GridFS) là chuẩn hơn.
Kết luận
Không có nghĩa là điều này là toàn diện, nhưng bạn có thể quyết định tùy chọn nào phù hợp nhất với nhu cầu của mình.
- DDP là đơn giản nhất và bám vào nguyên lý Meteor cốt lõi nhưng dữ liệu cồng kềnh hơn, không nén được trong quá trình truyền, không thể truy xuất. Nhưng tùy chọn này có thể tốt nếu bạn chỉ cần các tệp nhỏ.
- XHR được kết hợp với hệ thống tệp là cách 'truyền thống'. API ổn định, nhanh, 'có thể phát trực tuyến', có thể nén, có thể truy cập (ETag, v.v.), nhưng cần nằm trong một thư mục riêng biệt
- XHR được kết hợp với GridFS , bạn nhận được lợi ích của bộ đại diện, có thể mở rộng, không chạm vào dir hệ thống tệp, tệp lớn và nhiều tệp nếu hệ thống tệp hạn chế số lượng, cũng có thể nén được. Tuy nhiên, API không ổn định, bạn gặp lỗi trong nhiều lần ghi, đó là s..l..o..w ..
Hy vọng rằng sớm thôi, DDP sao băng có thể hỗ trợ gzip, caching, v.v. và GridFS có thể nhanh hơn ...