最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Uploading files using Skipper with Sails.js v0.10 - how to retrieve new file name - Stack Overflow

programmeradmin3浏览0评论

I am upgrading to Sails.js version 0.10 and now need to use Skipper to manage my file uploads.

When I upload a file I generate a new name for it using a UUID, and save it in the public/files/ folder (this will change when I've got this all working but it's good for testing right now)

I save the original name, and the uploaded name + path into a Mongo database.

This was all quite straightforward under Sails v0.9.x but using Skipper I can't figure out how to read the new file name and path. (Obviously if I could read the name I could construct the path though so it's really only the name I need)

My Controller looks like this

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: {}

};

How do I do this?

I am upgrading to Sails.js version 0.10 and now need to use Skipper to manage my file uploads.

When I upload a file I generate a new name for it using a UUID, and save it in the public/files/ folder (this will change when I've got this all working but it's good for testing right now)

I save the original name, and the uploaded name + path into a Mongo database.

This was all quite straightforward under Sails v0.9.x but using Skipper I can't figure out how to read the new file name and path. (Obviously if I could read the name I could construct the path though so it's really only the name I need)

My Controller looks like this

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: {}

};

How do I do this?

Share Improve this question asked Jun 1, 2014 at 5:23 Dave SagDave Sag 13.5k14 gold badges91 silver badges139 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 10

I've worked this out now and thought I'd share my solution for the benefit of others struggling with similar issues.

The solution was to not use skipper-disk at all but to write my own custom receiver. I've created this as a Sails Service object.

So in file 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;
    },
    pleted: 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.pleted) opts.pleted = options.pleted;

  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.pleted({
        name: file.filename,
        size: file.size,
        localName: newFilename,
        path: fileSavePath
      }, done);
    });
  };

  return documentReceiver;
}

and then my controller just became (in 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;
          },
          pleted: 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: {}
};

I'm sure it can be improved further but this works perfectly for me.

The uploaded file object contains all data you need:

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]));
    }
  }
});
发布评论

评论列表(0)

  1. 暂无评论