表达文件上传错误和几个问题

时间:2015-07-28 11:19:26

标签: node.js express

我正在使用multiparty来处理文件上传。

在我的控制器中,我有

exports.uploadFile = function(req, res, next) {

    if (req.files && req.files.file) {
        var file = req.files.file
        var tmpPath = file.path
        var targetDirectory = dir.public + '/upload'
        var targetPath = dir.public + '/upload' + '/' + file.name

        if (file.size > config.uploadFileMaxSize) {
            fs.unlink(tmpPath, function(err) {
                if (err)
                    return next(err)
                return next(helper.getAbuseError('file is too large'))
            })
        }
        else {
            fs.mkdir(targetDirectory, function(err) {
                if (err && err.code !== 'EEXIST')
                    return next(err)
                fs.rename(tmpPath, targetPath, function(err) {
                    if (err)
                        return next(err)
                    fs.unlink(tmpPath, function(err) {
                        if (err)
                            return next(err)
                        return res.json(helper.dataAppendedWithMessage({path: targetPath}, 'success', 'file uploaded'))
                    })
                })
            })
        }

    }
}

在我的路由器中:

restRouter.post(helper.serverUrl.common.uploadFile,
    helper.permission.middleware(helper.accessList.uploadFile),
    multiparty(), //Note: only used in file uploads
    commonController.uploadFile)

有几个问题。

  1. 无法使用mkdir。在取消链接时错误中断

    ENOENT,unlink' /var/folders/5p/qqz2vyhd3yqb_334h6fj7_y00000gq/T/O3BTOo3rXdKRT3Zfs5JPUpn8.png'

  2. 为什么上述tmpPath位于/var/folders下?从我阅读的所有教程中,它应存储在[app_root]/tmp

  3. 除了检查文件大小外,我如何才允许图像,音频和视频?我需要支持所有可以嵌入和标记的类型,并拒绝其他所有类型。我知道有一个file.type值,但我不想硬编码。如何检查HTML5标签中是否可以使用文件类型?

  4. 在我的成功回调中,我发送回{path: targetPath}以供客户端标记使用。但是targetPath是/path/to/app_root/public/upload/xxx.png,我怎么才能回发/public/upload/xxx.png?我当然可以搜索public并删除/path/to/app_root/但代码会变得混乱。还有其他优雅的方法吗?

  5. 可以提前拒绝大文件吗?只有在/tmp存储文件后才会调用此函数,攻击者可以通过简单地上传1000GB文件轻松填充我的存储空间

2 个答案:

答案 0 :(得分:1)

以下是几个答案:

  1. fs.rename()将文件从A重命名为B,但不会复制文件。因此,在成功重命名后,路径A不再存在,因此您不能(/不必)取消链接;
  2. 不确定为什么会这样。 /var/folders/XXX/YYY/T包含“每用户临时文件和缓存”(请参阅$ man hier),但多方使用os.tmpDir(),至少在我的Mac上返回/tmp (正如预期的那样);
  3. 简而言之:你不能,除非你能找到一个提供这些类型的模块,我怀疑它存在。您可以检查image/*video/*audio/*的内容类型,
  4. 我认为你仍然需要手动完成:

    var filePath   = '/upload/' + file.name;
    var targetPath = dir.public + filePath;
    ...
    res.json({ path: filePath });
    
  5. 您传递给connect-multiparty的所有选项都会传到multiparty引擎盖下,ASCII选项。这会将所有文件的上传限制为某个限制,但如果您只上传一个文件,它也应该有效;

答案 1 :(得分:0)

我给你一个功能可能这可以帮助你上传文件虽然我已经完成了图片上传,但几乎没有修改,这也可能对你有用..

    handleFileUpload: function(req) {

            if(!req.files || !req.files.place_img || !req.files.place_img.name) {
                return req.body.currentPicture || '';
            }
            var data = fs.readFileSync(req.files.place_img.path);
            var fileName = req.files.place_img.name;
            var uid = crypto.randomBytes(10).toString('hex');
            var dir = __dirname +"/../public/uploads/" + uid;
            fs.mkdirSync(dir, '0777');
            fs.writeFileSync(dir + "/" + fileName, data);
            return '/uploads/' + uid + "/" + fileName;
}