2013-06-12 39 views
5

Tôi cần cho phép người dùng ứng dụng của tôi tải xuống tệp bằng Meteor. Hiện tại những gì tôi làm là khi người dùng yêu cầu tải xuống tệp tôi nhập vào bộ sưu tập "fileRequests" trong Mongo một tài liệu có vị trí tệp và dấu thời gian của yêu cầu và trả lại ID của yêu cầu mới được tạo. Khi máy khách nhận được ID mới, nó sẽ chuyển tới mydomain.com/uploads/:id. sau đó tôi sử dụng một cái gì đó như thế này để đánh chặn các yêu cầu trước khi Meteor làm:Cách sử dụng createReadStream của hệ thống tập tin bằng bộ định tuyến Meteor (NodeJS)

var connect = Npm.require("connect"); 
var Fiber = Npm.require("fibers"); 
var path = Npm.require('path'); 
var fs = Npm.require("fs"); 
var mime = Npm.require("mime"); 

__meteor_bootstrap__.app 
    .use(connect.query()) 
    .use(connect.bodyParser()) //I add this for file-uploading 
    .use(function (req, res, next) { 
     Fiber(function() { 

      if(req.method == "GET") { 
       // get the id here, and stream the file using fs.createReadStream(); 
      } 
      next(); 
     }).run(); 
    }); 

tôi kiểm tra để chắc chắn rằng các tập tin yêu cầu đã được thực hiện chưa đầy 5 giây, và tôi ngay lập tức xóa các văn bản yêu cầu sau khi tôi đã truy vấn nó .

Tính năng này hoạt động và an toàn (đủ) tôi nghĩ. Không ai có thể thực hiện một yêu cầu mà không cần đăng nhập và 5 giây là một cửa sổ khá nhỏ để ai đó có thể nâng cấp URL yêu cầu đã tạo nhưng tôi không cảm thấy đúng với giải pháp của mình. Nó cảm thấy bẩn!

Vì vậy, tôi đã cố gắng sử dụng Meteor-Router để thực hiện điều tương tự. Bằng cách đó, tôi có thể kiểm tra xem họ có đăng nhập chính xác mà không cần phải làm việc trong 5 giây để giải quyết vấn đề lừa đảo trên thế giới không.

Vì vậy, đây là đoạn code tôi viết cho rằng:

Meteor.Router.add('/uploads/:id', function(id) { 

    var path = Npm.require('path'); 
    var fs = Npm.require("fs"); 
    var mime = Npm.require("mime"); 

    var res = this.response; 

    var file = FileSystem.findOne({ _id: id }); 

    if(typeof file !== "undefined") { 
     var filename = path.basename(file.filePath); 
     var filePath = '/var/MeteorDMS/uploads/' + filename; 

     var stat = fs.statSync(filePath); 

     res.setHeader('Content-Disposition', 'attachment; filename=' + filename); 
     res.setHeader('Content-Type', mime.lookup(filePath)); 
     res.setHeader('Content-Length', stat.size); 

     var filestream = fs.createReadStream(filePath); 

     filestream.pipe(res); 

     return; 
    } 
}); 

này sẽ rất tốt, phù hợp ngay với phần còn lại của mã và rất dễ dàng để đọc, không có hack tham gia, NHƯNG! Nó không hoạt động! Trình duyệt quay và quay và không bao giờ biết phải làm gì. Tôi có thông báo lỗi ZERO sắp xuất hiện. Tôi có thể tiếp tục sử dụng ứng dụng trên các tab khác. Tôi không biết nó đang làm gì, nó không bao giờ ngừng "tải". Nếu tôi khởi động lại máy chủ, tôi nhận được một tập tin 0 byte với tất cả các tiêu đề chính xác, nhưng tôi không nhận được dữ liệu.

Bất kỳ trợ giúp nào được đánh giá cao !!

EDIT:

Sau khi đào bới xung quanh một chút nữa, tôi nhận thấy rằng cố gắng để biến đối tượng response vào một đối tượng JSON kết quả trong một lỗi cấu trúc tròn.

Bây giờ điều thú vị về điều này là khi tôi nghe luồng cho sự kiện "dữ liệu" và cố gắng xâu chuỗi đối tượng phản hồi, tôi không nhận được lỗi đó. Nhưng nếu tôi cố gắng làm điều tương tự trong giải pháp đầu tiên của tôi (nghe "dữ liệu" và xâu chuỗi câu trả lời) tôi lại gặp lỗi.

Vì vậy, sử dụng giải pháp Meteor-Router có gì đó đang xảy ra với đối tượng phản hồi. Tôi cũng nhận thấy rằng về sự kiện "data" response.finished được gắn cờ là true.

filestream.on('data', function(data) { 
    fs.writeFile('/var/MeteorDMS/afterData', JSON.stringify(res)); 
}); 
+0

Chúng tôi có cùng một vấn đề. Chúng tôi phát hiện ra rằng có hai vấn đề trong một. Bạn cần thực hiện 'return false;', điều này sẽ gọi 'next()' của middleware ở đây: https://github.com/tmeasday/meteor-router/blob/master/lib/router_server.js#L86, nhưng 'pipe() 'cũng không hoạt động. Chúng tôi vẫn đang điều tra. – nalply

Trả lời

1

Bộ định tuyến Meteor cài đặt phần mềm trung gian để thực hiện định tuyến. Tất cả phần mềm trung gian kết nối hoặc PHẢI gọi next() (chính xác một lần) để cho biết rằng phản hồi chưa được giải quyết hoặc PHẢI giải quyết phản hồi bằng cách gọi res.end() hoặc bằng đường ống đến phản hồi. Nó không được phép làm cả hai.

Tôi đã nghiên cứu mã nguồn của phần mềm trung gian (xem bên dưới). Chúng tôi thấy rằng chúng tôi có thể trả lại false để yêu cầu phần mềm trung gian gọi next(). Điều này có nghĩa là chúng tôi tuyên bố rằng tuyến đường này không giải quyết được phản hồi và chúng tôi muốn cho các phần mềm trung gian khác thực hiện công việc của họ.Hoặc chúng tôi có thể trả lại tên mẫu, văn bản, mảng [status, text] hoặc mảng [status, headers, text] và phần mềm trung gian sẽ giải quyết phản hồi thay mặt chúng tôi bằng cách gọi số res.end() bằng cách sử dụng dữ liệu chúng tôi đã trả về.

Tuy nhiên, bằng đường ống đến phản hồi, chúng tôi đã giải quyết phản hồi. Bộ định tuyến Meteor không được gọi next() cũng không phải res.end().

Chúng tôi giải quyết vấn đề bằng cách giả mạo bộ định tuyến Meteor và thực hiện một thay đổi nhỏ. Chúng tôi thay thế else trong dòng 87 (sau khi if (output === false)) bằng cách:

else if (typeof(output)!="undefined") { 

Xem các cam kết với sha 8d8fc23d9c ở ngã ba của tôi.

Bằng cách này, return; trong phương thức tuyến đường sẽ yêu cầu bộ định tuyến thực hiện không có gì. Tất nhiên bạn đã giải quyết phản ứng bằng đường ống đến nó.


Source code của middleware như trong cam kết với sha f910a090ae:

// hook up the serving 
__meteor_bootstrap__.app 
    .use(connect.query()) // <- XXX: we can probably assume accounts did this 
    .use(this._config.requestParser(this._config.bodyParser)) 
    .use(function(req, res, next) { 
    // need to wrap in a fiber in case they do something async 
    // (e.g. in the database) 
    if(typeof(Fiber)=="undefined") Fiber = Npm.require('fibers'); 

    Fiber(function() { 
     var output = Meteor.Router.match(req, res); 

     if (output === false) { 
     return next(); 
     } else { 
     // parse out the various type of response we can have 

     // array can be 
     // [content], [status, content], [status, headers, content] 
     if (_.isArray(output)) { 
      // copy the array so we aren't actually modifying it! 
      output = output.slice(0); 

      if (output.length === 3) { 
      var headers = output.splice(1, 1)[0]; 
      _.each(headers, function(value, key) { 
       res.setHeader(key, value); 
      }); 
      } 

      if (output.length === 2) { 
      res.statusCode = output.shift(); 
      } 

      output = output[0]; 
     } 

     if (_.isNumber(output)) { 
      res.statusCode = output; 
      output = ''; 
     } 

     return res.end(output); 
     } 
    }).run(); 
    }); 
+0

** NOTE **: Trong khi đó Meteor 6.5 bị tắt và bộ định tuyến đã thay đổi của tôi không hoạt động nữa. – nalply

Các vấn đề liên quan