使用带有Sails.js v0.10的Skipper上传文件 - 如何检索新文件名

时间:2014-06-01 05:23:53

标签: javascript file-upload sails.js multifile-uploader skipper

我正在升级到Sails.js版本0.10,现在需要使用Skipper来管理我的文件上传。

当我上传文件时,我使用UUID为它生成一个新名称,并将其保存在public / files /文件夹中(当我将这一切全部工作时,这将会改变,但它会很好现在进行测试)

我将原始名称和上传的名称+路径保存到Mongo数据库中。

Sails v0.9.x下这一切都非常简单,但使用Skipper我无法弄清楚如何阅读新文件名和路径。 (显然,如果我能读出我可以构建路径的名称,那么它真的只是我需要的名字)

我的控制器看起来像这样

var uuid = require('node-uuid'),
    path = require('path'),
    blobAdapter = require('skipper-disk');

module.exports = {

  upload: function(req, res) {

    var receiver = blobAdapter().receive({
          dirname: sails.config.appPath + "/public/files/",
          saveAs: function(file) {
            var filename = file.filename,
                newName = uuid.v4() + path.extname(filename);
            return newName;
          }
        }),
        results = [];

    req.file('docs').upload(receiver, function (err, files) {
      if (err) return res.serverError(err);
      async.forEach(files, function(file, next) {
        Document.create({
          name: file.filename,
          size: file.size,
          localName: // ***** how do I get the `saveAs()` value from the uploaded file *****,
          path: // *** and likewise how do i get the path ******
        }).exec(function(err, savedFile){
          if (err) {
            next(err);
          } else {
            results.push({
              id: savedFile.id,
              url: '/files/' + savedFile.localName
            });
            next();
          }
        });
      }, function(err){
        if (err) {
          sails.log.error('caught error', err);
          return res.serverError({error: err});
        } else {
          return res.json({ files: results });
        }
      });
    });
  },

  _config: {}

};

我该怎么做?

2 个答案:

答案 0 :(得分:10)

我现在已经解决了这个问题,并且认为我会分享我的解决方案,以帮助那些在类似问题上苦苦挣扎的人。

解决方案是完全不使用skipper-disk,而是编写我自己的自定义receiver。我已将其创建为Sails Service对象。

所以在文件api/services/Uploader.js

// Uploader utilities and helper methods
// designed to be relatively generic.

var fs = require('fs'),
    Writable = require('stream').Writable;

exports.documentReceiverStream = function(options) {
  var defaults = {
    dirname: '/dev/null',
    saveAs: function(file){
      return file.filename;
    },
    completed: function(file, done){
      done();
    }
  };

  // I don't have access to jQuery here so this is the simplest way I
  // could think of to merge the options.
  opts = defaults;
  if (options.dirname) opts.dirname = options.dirname;
  if (options.saveAs) opts.saveAs = options.saveAs;
  if (options.completed) opts.completed = options.completed;

  var documentReceiver = Writable({objectMode: true});

  // This `_write` method is invoked each time a new file is received
  // from the Readable stream (Upstream) which is pumping filestreams
  // into this receiver.  (filename === `file.filename`).
  documentReceiver._write = function onFile(file, encoding, done) {
    var newFilename = opts.saveAs(file),
        fileSavePath = opts.dirname + newFilename,
        outputs = fs.createWriteStream(fileSavePath, encoding);
    file.pipe(outputs);

    // Garbage-collect the bytes that were already written for this file.
    // (called when a read or write error occurs)
    function gc(err) {
      sails.log.debug("Garbage collecting file '" + file.filename + "' located at '" + fileSavePath + "'");

      fs.unlink(fileSavePath, function (gcErr) {
        if (gcErr) {
          return done([err].concat([gcErr]));
        } else {
          return done(err);
        }
      });
    };

    file.on('error', function (err) {
      sails.log.error('READ error on file ' + file.filename, '::', err);
    });

    outputs.on('error', function failedToWriteFile (err) {
      sails.log.error('failed to write file', file.filename, 'with encoding', encoding, ': done =', done);
      gc(err);
    });

    outputs.on('finish', function successfullyWroteFile () {
      sails.log.debug("file uploaded")
      opts.completed({
        name: file.filename,
        size: file.size,
        localName: newFilename,
        path: fileSavePath
      }, done);
    });
  };

  return documentReceiver;
}

然后我的controller刚刚成为(api/controllers/DocumentController.js

var uuid = require('node-uuid'),
    path = require('path');

module.exports = {

  upload: function(req, res) {

    var results = [],
        streamOptions = {
          dirname: sails.config.appPath + "/public/files/",
          saveAs: function(file) {
            var filename = file.filename,
                newName = uuid.v4() + path.extname(filename);
            return newName;
          },
          completed: function(fileData, next) {
            Document.create(fileData).exec(function(err, savedFile){
              if (err) {
                next(err);
              } else {
                results.push({
                  id: savedFile.id,
                  url: '/files/' + savedFile.localName
                });
                next();
              }
            });
          }
        };

    req.file('docs').upload(Uploader.documentReceiverStream(streamOptions),
      function (err, files) {
        if (err) return res.serverError(err);

        res.json({
          message: files.length + ' file(s) uploaded successfully!',
          files: results
        });
      }
    );
  },

  _config: {}
};

我确信它可以进一步改进,但这对我来说非常适合。

答案 1 :(得分:4)

上传的文件对象包含您需要的所有数据:

req.file('fileTest').upload({
  // You can apply a file upload limit (in bytes)
  maxBytes: maxUpload,
  adapter: require('skipper-disk')
}, function whenDone(err, uploadedFiles) {
  if (err) {
    var error = {  "status": 500, "error" : err };
    res.status(500);
    return res.json(error);
  } else {
    for (var u in uploadedFiles) {
      //"fd" contains the actual file path (and name) of your file on disk
      fileOnDisk = uploadedFiles[u].fd;
      // I suggest you stringify the object to see what it contains and might be useful to you
      console.log(JSON.stringify(uploadedFiles[u]));
    }
  }
});