MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Meteor:tải tệp từ máy khách lên bộ sưu tập Mongo so với hệ thống tệp và GridFS

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

  1. Cách đơn giản, không hack, không có gói bổ sung
  2. Bám sát nguyên tắc Dữ liệu trên dây

Nhược điểm

  1. 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
  2. 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?)
  3. Không có bộ nhớ đệm
  4. Chưa có gzip hoặc nén
  5. 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

  1. 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
  2. Bộ đệm mảng ít cồng kềnh hơn so với chuỗi base64
  3. 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ì
  4. 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)
  5. Có thể truy cập và gzip

Nhược điểm

  1. 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
    Tôi chỉ sử dụng xhr =new XMLHttpRequest (), thay vì HTTP.call ('POST') vì HTTP.call hiện tại trong Meteor chưa thể gửi bộ đệm mảng (chỉ cho tôi nếu tôi sai).
  2. / 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

  1. 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
  2. Không cần phải lo lắng về việc xóa tên tệp
  3. 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.
  4. Không cần triển khai bất kỳ gói nào khác
  5. Có thể lưu trữ và có thể được giải nén
  6. 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
  7. Sử dụng đường dẫn để giảm quá tải bộ nhớ

Nhược điểm

  1. 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
  2. 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.
  3. 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
  4. 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ệp
file 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 ...



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Cách trả về dữ liệu JSON từ php MongoCursor

  2. Sắp xếp theo trường ảo trong mongoDB (mongoose)

  3. Có thể truy vấn MongoDB nhiều lần cho mỗi yêu cầu không?

  4. Làm cách nào tôi có thể viết tập hợp mà không vượt quá kích thước tài liệu tối đa?

  5. Giám sát &Quản lý hoạt động của MongoDB 4.0 với ClusterControl