如何使用 multer 存储具有文件扩展名的文件?

Posted

技术标签:

【中文标题】如何使用 multer 存储具有文件扩展名的文件?【英文标题】:How to store a file with file extension with multer? 【发布时间】:2015-10-14 01:48:30 【问题描述】:

设法将我的文件存储在一个文件夹中,但它们存储时没有文件扩展名。

有谁知道我将如何存储带有文件扩展名的文件?

【问题讨论】:

【参考方案1】:

简单的辅助函数,维护 multer 生成的唯一文件名并添加从 mimetype 解析的扩展名:

只需传递multer返回的对象


const fs = require('fs');

function renameWithExt(file) 
  const ext = file.mimetype.split('/')[1]; // parse the extension type

  fs.rename(`$file.path`, `$file.path.$ext`, () => 
    console.log(`File: $file.filename renamed with extension '.$ext'`);
  );


renameWithExt(req.file);

【讨论】:

【参考方案2】:

const multer = require('multer');
const uuid = require('uuid/v1');

const MIME_TYPE_MAP = 
  'image/png': 'png',
  'image/jpeg': 'jpeg',
  'image/jpg': 'jpg'
;

const fileUpload = multer(
  limits: 500000,
  storage: multer.diskStorage(
    destination: (req, file, cb) => 
      cb(null, 'uploads/images');
    ,
    filename: (req, file, cb) => 
      const ext = MIME_TYPE_MAP[file.mimetype];
      cb(null, uuid() + '.' + ext);
    
  ),
  fileFilter: (req, file, cb) => 
    const isValid = !!MIME_TYPE_MAP[file.mimetype];
    let error = isValid ? null : new Error('Invalid mime type!');
    cb(error, isValid);
  
);

module.exports = fileUpload;

【讨论】:

【参考方案3】:

const multer = require('multer'); const uuid = require('uuid/v1');

const MIME_TYPE_MAP = 
  'image/png': 'png',
  'image/jpeg': 'jpeg',
  'image/jpg': 'jpg'
;

const fileUpload = multer(
  limits: 500000,
  storage: multer.diskStorage(
    destination: (req, file, cb) => 
      cb(null, 'uploads/images');
    ,
    filename: (req, file, cb) => 
      const ext = MIME_TYPE_MAP[file.mimetype];
      cb(null, uuid() + '.' + ext);
    
  ),
  fileFilter: (req, file, cb) => 
    const isValid = !!MIME_TYPE_MAP[file.mimetype];
    let error = isValid ? null : new Error('Invalid mime type!');
    cb(error, isValid);
  
);

module.exports = fileUpload;

【讨论】:

【参考方案4】:

可以这样操作...简单易懂

// validate uploaded files
const FILE_TYPE_MAP = 
  // mime type
  "image/png": "png",
  "image/jpeg": "jpeg",
  "image/jpg": "jpg",
;
const storage = multer.diskStorage(
  destination: function (req, file, cb) 
    cb(null, "public/uploads");
  ,
  filename: function (req, file, cb) 
    const filename = file.originalname.replace(" ", "-");
    const extension = FILE_TYPE_MAP[file.mimetype]
    cb(null, `$filename-$Date.now().$extension`);
  ,
);

【讨论】:

您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center。 这行得通。但是,我上传 .jpeg 还是 .jpg 并不重要。我总是以扩展名 .jpeg 结尾。【参考方案5】:

我用这个方法,效果很好。

我以这种格式存储文件: FieldName+Date+Extension => Profile1621416613594.jpg

var multer = require('multer');
    
var storage = multer.diskStorage(
    destination: function (req,file,cb)
        cb(null, './uploads')
    ,
    filename: function (req,file,cb)
        cb(null,file.fieldname+'-'+Date.now()+'.'+file.mimetype.split('/').reverse()[0]);
    ,
);

var upload = multer(storage: storage); 

【讨论】:

【参考方案6】:

可以这样做:

var storage = multer.diskStorage(
    destination: function (req, file, cb) 
        cb(null, config.DIR)
    ,
    filename: function (req, file, cb) 
        let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
        cb(null, Date.now() + ext)
    
);
const upload = multer(
    storage: storage
).any();

【讨论】:

你为什么提到像2018,它只是一个子字符串吧 我不记得为什么提到 2018 年了,因为现在是 2019 年。 这会受到文件名中没有扩展名的文件名的影响。您应该以某种方式使用 mimetype。【参考方案7】:
import multer from 'multer';
import * as shortid from 'shortid';
import * as mime from 'mime-types';

const storage = multer.diskStorage(
  destination: function (req,file,cb) 
    cb(null, '/path/to/uploads/');
  ,
  filename: function (req,file,cb) 
    /* generates a "unique" name - not collision proof but unique enough for small sized applications */
    let id = shortid.generate();
    /* need to use the file's mimetype because the file name may not have an extension at all */
    let ext = mime.extension(file.mimetype);
    cb(null, `$id.$ext`);
  
);

编辑

shortid 已被弃用,您应该使用 nanoid。

import multer from 'multer';
import * as nanoid from 'nanoid';
import * as mime from 'mime-types';

const storage = multer.diskStorage(
  destination: function (req,file,cb) 
    cb(null, '/path/to/uploads/');
  ,
  filename: function (req,file,cb) 
    /* generates a "unique" name - not collision proof but unique enough for small sized applications */
    let id = nanoid();
    /* need to use the file's mimetype because the file name may not have an extension at all */
    let ext = mime.extension(file.mimetype);
    cb(null, `$id.$ext`);
  
);

【讨论】:

【参考方案8】:

我喜欢将原始文件名用于 SEO。这需要更多检查是否已存在同名文件。 此外,扩展解析只需几个步骤即可提供最大的灵活性。

var storage = multer.diskStorage(
  destination: function (req, file, cb) 
    cb(null, 'uploads/')
  ,
  filename: function (req, file, cb) 
    // try to get extension from original file name
    var lioDot = file.originalname.lastIndexOf('.');
    if (lioDot !== -1) 
      // I like to use original upload filename for SEO but first lets clean it 
      var newName = file.originalname.substring(0, lioDot).replace(/([^a-z0-9]+)/gi, '-');
      var ext = file.originalname.substring(lioDot, file.originalname.length);
     else 
      var newName = file.originalname.replace(/([^a-z0-9]+)/gi, '-');
      // try to get extension from mime type string
      var extArray = file.mimetype.split("/");
      var ext = extArray[extArray.length - 1];
      // mime type extension resolving by pure string extraction is not accurate for a lot of types
      // https://www.freeformatter.com/mime-types-list.html
      // it's usually fine for ext strings up to 4 characters, png, jpeg, gif, bmp, tiff ..
      if (ext > 4) 
        // other mime types you would like to support
        var mimetypes =  'vnd.openxmlformats-officedocument.wordprocessingml.document': '.docx' ;
        if (mimetypes.hasOwnProperty(ext)) ext = mimetypes[ext];
      
    

    var newFullName = newName + ext;
    var i = 0;
    // we need to check if the file with the same name already exists
    // if it exists then we're adding something to make it unique 
    while (fs.existsSync(process.env.PWD + '/uploads/' + newFullName)) 
      newFullName = newName + '-' + ++i + ext;
    
    cb(null, newFullName);
  
)

const upload = multer( storage: storage );

【讨论】:

【参考方案9】:

文件扩展名可以是动态的。 这是解决方案

const path = require('path'); // path for cut the file extension
const storage = multer.diskStorage(
    destination: function (req, file, cb) 
      cb(null, 'uploads')
    ,
    filename: function (req, file, cb) 
      cb(null, 'upload_at_' + Date.now() + path.extname(file.originalname))
    
  )

【讨论】:

【参考方案10】:

我使用这个小技巧来获取文件扩展名,并作为一种解决方法来规避当有人上传具有相似文件名的文件两次或服务器中存在的文件时可能出现的问题。

const path = require('path');
const crypto = require('crypto');

let upload = multer(
storage: multer.diskStorage(
    destination: (req, file, cb) => 
        cb(null, path.join(__dirname, '../uploads'))
    ,
    filename: (req, file, cb) => 
        // randomBytes function will generate a random name
        let customFileName = crypto.randomBytes(18).toString('hex')
        // get file extension from original file name
        let fileExtension = path.extname(file.originalname).split('.')[1];
        cb(null, customFileName + '.' + fileExtension)
    
  )
)

【讨论】:

如果文件名中有多个点怎么办? @JDrake 检查更新的答案,我更新了解决方案【参考方案11】:

我有一个解决方法来添加正确的文件扩展名。如果你使用path节点模块

var multer = require('multer');
var path = require('path')

var storage = multer.diskStorage(
  destination: function (req, file, cb) 
    cb(null, 'uploads/')
  ,
  filename: function (req, file, cb) 
    cb(null, Date.now() + path.extname(file.originalname)) //Appending extension
  
)

var upload = multer( storage: storage );

【讨论】:

这是最好的解决方案 这是一个绝妙的解决方案,感谢您分享它 这个解决方案有问题,它不考虑没有文件扩展名的文件。在这种情况下,您需要使用文件的 mime 类型。 我同意,这是最好的解决方案。为了找出正确的扩展名,您可以使用 mime-types。 npmjs.com/package/mime-types 这应该是公认的答案,谢谢!!【参考方案12】:

一种面向对象的方式来存储具有唯一名称的图像

// image.service.ts
import  diskStorage, StorageEngine  from "multer";

class ImageStorageService 

    storage: StorageEngine
    constructor() 
        const MIME_TYPE_MAP = 
            'image/png': 'png',
            'image/jpeg': 'jpg',
            'image/jpg': 'jpg'
        

        this.storage = diskStorage(
            destination: (req, file, callback) => 
                const isValid = MIME_TYPE_MAP[file.mimetype]
                let error = new Error(`Invalid mime type`)
                if (isValid)
                    error = null

                //app.use(express.static(path.join(`$__dirname/assets`)))
                callback(error, 'assets/images')
            ,
            filename: (req, file, callback) => 
                let currentFileName: string = file.originalname.substr(0, file.originalname.lastIndexOf('.'))
                const name = currentFileName.toLowerCase().split(' ').join('-')
                const ext = MIME_TYPE_MAP[file.mimetype]
                callback(null, `$name-$Date.now().$ext`)
            
        )
    


export const ImageStorage = new ImageStorageService().storage

然后在你的一条路线中

import  ImageStorage  from "./services/image-storage.service";

this.router.post('/signup', multer( storage: ImageStorage ).single('image'), async (req, res, next) => 
    let img_url: string
    if (req.file) 
        const url: string = `$req.protocol:\/\/$req.get('host')`
        img_url = url + '/images/' + req.file.filename
        //http://localhost:3000/images/penguins-1548339248380.jpg
    
)

【讨论】:

【参考方案13】:

已经回答的代码中可能存在一些问题。

可能存在文件没有扩展名的某些情况。 不应有 upload.any() 用法。它容易受到攻击者的攻击 上传函数应该不是全局的 .

为了更好的安全性,我编写了以下代码。

var storage = multer.diskStorage(
    destination: function (req, file, cb) 

        cb(null, 'temp/')
    ,
    filename: function (req, file, cb) 
        let ext = ''; // set default extension (if any)
        if (file.originalname.split(".").length>1) // checking if there is an extension or not.
            ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
        cb(null, Date.now() + ext)
    
)
var upload = multer( storage: storage );

用它来上传

// using only single file object name (HTML name attribute)
// May use upload.array(["file1","file2"]) for more than one
app.post('/file_upload', upload.single("file"), function (req,res) 
    //console.log(req.body, 'Body');
    console.log(req.file, 'file');
    res.send("cool");
)

【讨论】:

感谢您在这里输入,但这并不能回答问题。如果您想将这些知识存储在 *** 上,请考虑发布一个新问题并自行回答。在做之前还要阅读帮助部分。【参考方案14】:

我就是这样的

var multer  = require('multer');

var storage = multer.diskStorage(
  destination: function (req, file, cb) 
    cb(null, './public/uploads/img/')
  ,
  filename: function (req, file, cb) 
    let ext = file.originalname.substring(file.originalname.lastIndexOf('.'), file.originalname.length);
    cb(null, Date.now() + ext);
  
)

var upload = multer( storage: storage ).single('eventimage');

【讨论】:

如果文件名中有多个点怎么办? 文件名没有扩展名怎么办?【参考方案15】:

我从 file.mimetype 获得了文件扩展名。 我拆分 mimetype 并从中获取文件扩展名 请尝试以下功能。

let storage = multer.diskStorage(
  destination: function (req, file, cb) 
    cb(null, './uploads')
  ,
  filename: function (req, file, cb) 
    let extArray = file.mimetype.split("/");
    let extension = extArray[extArray.length - 1];
    cb(null, file.fieldname + '-' + Date.now()+ '.' +extension)
  
)
const upload = multer( storage: storage )

【讨论】:

如果您尝试上传 .docx 文件而不是此方法失败,则此解决方案存在问题【参考方案16】:

来自文档:“Multer 不会为您附加任何文件扩展名,您的函数应返回带有文件扩展名的完整文件名。”

添加扩展的方法如下:

var multer = require('multer');

var storage = multer.diskStorage(
  destination: function (req, file, cb) 
    cb(null, 'uploads/')
  ,
  filename: function (req, file, cb) 
    cb(null, Date.now() + '.jpg') //Appending .jpg
  
)

var upload = multer( storage: storage );

我建议使用mimetype 属性来确定扩展名。例如:

filename: function (req, file, cb) 
  console.log(file.mimetype); //Will return something like: image/jpeg

更多信息:https://github.com/expressjs/multer

【讨论】:

我遵循这个,但我的文件上传到我电脑的 C: 驱动器的上传文件夹下。如何将它们保存在我的项目目录中。 @kisor 你关注_dirname 这个答案只是指出 Multer 没有附加文件扩展名,但 OP 要求一种附加扩展名的方法。建议有效,但没有回答主要问题。

以上是关于如何使用 multer 存储具有文件扩展名的文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Multer 提交带有可选文件提交的表单?

使用 multer-storage-cloudinary 存储在两个不同的文件夹中

使用具有角度7平均堆栈的multer中间件上传文件不起作用

如何将具有特定扩展名的文件添加到化石存储库中?

具有清晰上传和调整大小的 Multer-S3

使用 Multer 上传文件并在解析和上传到数据库之前将其短暂存储在内存中