2013-06-12 2 views
5

Мне нужно разрешить пользователю моего приложения загружать файл с помощью Meteor. В настоящее время то, что я делаю, - это когда пользователь запрашивает загрузку файла, который я вхожу в коллекцию «fileRequests» в Mongo, документ с указанием местоположения файла и отметкой времени запроса и возвращает идентификатор вновь созданного запроса. Когда клиент получает новый идентификатор, он немедленно отправляется на mydomain.com/uploads/:id. Затем я использую что-то вроде этого, чтобы перехватить запрос до Метеора делает:Как использовать файловую систему createReadStream с Meteor router (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(); 
    }); 

проверить, чтобы убедиться, запрос файла был сделан менее чем за 5 секунд назад, и я сразу же удалить документ запроса после того, как я его опрошен ,

Это работает, и я уверен (достаточно). Никто не может сделать запрос без входа в систему, а 5 секунд - это довольно маленькое окно для того, чтобы кто-то смог переустановить созданный URL-адрес запроса, но я просто не чувствую себя хорошо с моим решением. Он чувствует себя грязным!

Итак, я попытался использовать Meteor-Router, чтобы выполнить то же самое. Таким образом, я могу проверить, правильно ли они вошли в систему, не делая 5 секунд открытой для мировой хитрости.

Так вот код, который я написал для этого:

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; 
    } 
}); 

Это выглядит большим, вписывается в остальной части кода и легко не читать, не хакерство участие, НО! Это не работает! Браузер вращается и вращается и никогда не знает, что делать. У меня появляются сообщения об ошибке ZERO. Я могу продолжать использовать приложение на других вкладках. Я не знаю, что он делает, он никогда не останавливает «загрузку». Если я перезапущу сервер, я получаю 0-байтовый файл со всеми правильными заголовками, но я не получаю данные.

Любая помощь очень ценится !!

РЕДАКТИРОВАТЬ:

После того, как рытье вокруг немного больше, я заметил, что попытки превратить объект ответа в результатах объекта JSON в круговой ошибки структуры.

Теперь интересная вещь в том, что, когда я слушаю поток для события «данные» и пытаюсь укрепить объект ответа, я не получаю эту ошибку. Но если я попытаюсь сделать то же самое в своем первом решении (прослушивание «данных» и строгая ответ), я снова получаю ошибку.

Так что, используя решение Meteor-Router, что-то происходит с объектом ответа. Я также заметил, что в событии «data» response.finished имеет значение true.

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

У нас такая же проблема. Мы выяснили, что в одном есть две проблемы. Вам нужно выполнить 'return false;', это вызывает «next()» промежуточного программного обеспечения здесь: https://github.com/tmeasday/meteor-router/blob/master/lib/router_server.js#L86, но 'pipe() 'тоже не работает. Мы все еще расследуем. – nalply

ответ

1

Маршрутизатор Meteor устанавливает промежуточное ПО для выполнения маршрутизации. Все связующее ПО Connect должно либо ДОЛЖНО называть next() (ровно один раз), чтобы указать, что ответ еще не установлен, или ДОЛЖЕН разрешить ответ, вызвав res.end() или по трубопроводу к ответу. Нельзя делать то и другое.

Я изучил исходный код промежуточного программного обеспечения (см. Ниже). Мы видим, что мы можем вернуть false, чтобы сообщить промежуточному программному обеспечению, чтобы позвонить next(). Это означает, что мы заявляем, что этот маршрут не урегулировал ответ, и мы хотели бы, чтобы другое промежуточное ПО выполняло свою работу.

Или мы можем вернуть имя шаблона, текст, массив [status, text] или массив [status, headers, text], и промежуточное программное обеспечение будет оседать ответ от нашего имени, по телефону res.end() используя данные, которые мы возвратили.

Однако, отвечая на ответ, мы уже установили ответ. Роутер Meteor не должен звонить next() или res.end().

Мы решили проблему путем разметки маршрутизатора Meteor и внесения небольших изменений. Мы заменили else в строке 87 (после if (output === false)) путем:

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

См коммит с ша 8d8fc23d9c в моей вилке.

В этом способе return; в способе маршрута скажет маршрутизатору ничего. Конечно, вы уже отреагировали на ответ, сославшись на него.


Исходный код промежуточного программного обеспечения, как в фиксации с ша 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

** ПРИМЕЧАНИЕ **: Между тем, Meteor 6.5 отключен, и мой измененный маршрутизатор больше не работает. – nalply

Смежные вопросы