Nodejs 利用formidable上传文件,并利用Date对象重命名的方法讨论

Nodejs 利用formidable上传文件,并利用Date对象重命名的方法讨论

本人新手,刚编程不久,刚学nodejs不久,请多指教。


直接入题了。日常中处理FROM的POST请求是很常见的。上传文件也是一个常见功能。编了一段代码,对上传的文件进行了储存和重命名,大家可以看看,不知道是不是一个不错的方案,还有没有错误或者值得优化的地方。另外,有没有上传相同的文件时(不管是不是同样的文件名),能不能不生成重复的文件呢?

function uploadfiles(res, req) {
console.log("Request handle 'uploadfiles' was called.");
var form = new formidable.IncomingForm();
console.log("about to parse");
form.parse(req, function(error, fields, files) {
	console.log("parsing done");
	var types = files.upload.name.split('.'); //将文件名以.分隔,取得数组最后一项作为文件后缀名。
	console.log(types);
	var date = new Date();
    var ms = Date.parse(date); //计算当前时间与1970年1月1日午夜相差的毫秒数 赋值给ms以确保文件名无重复。
    fs.renameSync(files.upload.path, "/tmp/files" + ms +"." + String(types[types.length-1]));
});	

9 回复

Node.js 利用 formidable 上传文件,并利用 Date 对象重命名的方法讨论

引言

作为一个初学者,我在学习 Node.js 的过程中遇到了一些问题。其中一个问题是如何处理表单上传文件的功能。我编写了一些代码来处理这个问题,并希望得到一些反馈和建议。

代码实现

下面是我目前的实现方法:

const formidable = require('formidable');
const fs = require('fs');

function uploadFiles(req, res) {
    console.log("Request handle 'uploadFiles' was called.");
    
    const form = new formidable.IncomingForm();
    console.log("about to parse");

    form.parse(req, (error, fields, files) => {
        console.log("parsing done");

        if (error) {
            console.error("Error parsing the form:", error);
            return res.status(500).send("Error uploading file.");
        }

        const { upload } = files;
        
        if (!upload) {
            return res.status(400).send("No file uploaded.");
        }

        const { path, name } = upload;
        const [fileName, ext] = name.split('.');
        
        // 获取当前时间戳
        const date = new Date();
        const timestamp = date.getTime();

        // 重命名文件并移动到目标目录
        const newPath = `/tmp/files/${timestamp}.${ext}`;
        fs.rename(path, newPath, (err) => {
            if (err) {
                console.error("Error renaming file:", err);
                return res.status(500).send("Error uploading file.");
            }
            
            console.log(`File renamed and moved to ${newPath}`);
            res.send("File uploaded successfully.");
        });
    });
}

代码说明

  1. 引入模块:我们首先引入 formidablefs 模块。
  2. 创建表单解析器:使用 formidable.IncomingForm() 创建一个表单解析器实例。
  3. 解析表单数据:通过 form.parse(req, callback) 方法解析上传的数据。
  4. 获取文件信息:从解析后的 files 对象中获取上传的文件信息。
  5. 重命名文件
    • 使用 Date.now()new Date().getTime() 获取当前时间戳,确保文件名唯一。
    • 将文件重命名为带有时间戳的新文件名。
  6. 移动文件:使用 fs.renameSync() 或异步的 fs.rename() 方法将文件移动到指定目录。
  7. 返回响应:根据操作结果返回相应的 HTTP 响应。

文件名重复问题

为了防止文件名重复,我们可以使用时间戳来生成唯一的文件名。这种方法简单有效,但可能不够理想,特别是在高并发情况下。更优的方法是使用 UUID 或其他唯一标识符。

结论

以上代码提供了一个基本的文件上传功能,并利用时间戳重命名文件以避免重复。希望这对你有所帮助!如果你有任何建议或改进意见,请告诉我。


简单的改写了一下 没有环境 未测试。没用用过那个什么form的东西,所以上传逻辑没有做修改。重命名修改了一下。

var crypto = require('crypto');

function isValidFileName(filename) { var split = filename.split(’.’); if (split.length < 2) { return false; } return true; }

function getExt(filename) { var split = filename.split(’.’); return split[split.length - 1]; }

function rand(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }

function randString(size) { var result = ‘’; var allChar = ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’;

size = size || 1;
while (size--) {
    result += allChar.charAt(rand(0, allChar.length - 1));
}

return result;

}

function sha1(str) { return crypto.createHash(‘sha1’).update(str).digest(‘hex’); }

function randFilename() { return sha1(randString(20) + new Date()); }

function uploadfiles(res, req) { var form = new formidable.IncomingForm(); form.parse(req, function(error, fields, files) { var ext = ‘’; if (isValidFileName(files.upload.name)) { ext = getExt(files.upload.name); } else { // 错误处理 } fs.renameSync(files.upload.path, “/tmp/files” + randFilename() +"." + ext); }); }

如果不担心性能,可以对文件做md5或sha1,这样是最可以靠的。

function randFilename() {
    return sha1(randString(rand(10, 100)) + new Date());
}

突然想起,这里可以进一步随机。

修改了一下函数名。还是没有测试。

var crypto = require('crypto');

function isValidFilename(filename) { var split = filename.split(’.’); if (split.length < 2) { return false; } return true; }

function getFilenameExt(filename) { var split = filename.split(’.’); return split[split.length - 1]; }

function rand(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); }

function randString(size) { var result = ‘’; var allChar = ‘0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ’

size = size || 1;
while (size--) {
    result += allChar.charAt(rand(0, allChar.length - 1));
}

return result;

}

function sha1(str) { return crypto.createHash(‘sha1’).update(str).digest(‘hex’); }

function randFilename() { return sha1(randString(rand(10, 100)) + new Date()); }

function uploadFile(res, req) { var form = new formidable.IncomingForm();

form.parse(req, function(error, fields, files) {
    var ext = '';
    
    if (isValidFilename(files.upload.name)) {
        ext = getFilenameExt(files.upload.name);
    } else {
        // 错误处理
    }
    
    fs.renameSync(files.upload.path, "/tmp/files" + randFilename() + "." + ext);
}); 

}

(res,req) 好像位置反了吧?

磁盘保存的时候,文件名加上传时间到毫秒,一般就没有重复可能.

在对上传文件命名上,我也是采用时间戳,精确到毫秒还是不行的,重复的几率存在。NodeJS提供了纳秒的API,可以精确到纳秒,这样文件名是一串数字,便于排序也排除了重名的可能。

在你的代码示例中,你已经实现了使用 formidable 模块来处理文件上传,并且通过日期生成唯一文件名来避免文件重名问题。这里是一些改进建议和示例代码,帮助你更好地处理文件上传及避免文件重复。

改进建议

  1. 错误处理:增加错误处理逻辑,确保在文件操作失败时可以进行适当处理。
  2. 目标目录创建:确保目标目录存在,如果不存在则创建它。
  3. 文件类型检查:在重命名文件之前,可以增加文件类型的验证逻辑。
  4. 防止重复文件上传:在保存文件前,检查文件是否已存在。

示例代码

const fs = require('fs');
const path = require('path');
const formidable = require('formidable');

function uploadFiles(req, res) {
    console.log("Request handle 'uploadFiles' was called.");

    const form = new formidable.IncomingForm();

    form.parse(req, (error, fields, files) => {
        if (error) {
            return res.status(500).send('Internal Server Error');
        }

        console.log("Parsing done");

        const file = files.upload;
        if (!file) {
            return res.status(400).send('No file uploaded.');
        }

        const date = new Date();
        const ms = Date.now(); // 获取当前时间戳
        const ext = path.extname(file.name); // 获取文件扩展名
        const newPath = path.join('/tmp/files', `${ms}${ext}`); // 构建新文件路径

        // 检查目标目录是否存在,若不存在则创建
        const targetDir = path.dirname(newPath);
        if (!fs.existsSync(targetDir)) {
            fs.mkdirSync(targetDir, { recursive: true });
        }

        // 检查文件是否已存在
        if (fs.existsSync(newPath)) {
            console.log(`File ${newPath} already exists.`);
            return res.status(409).send('File already exists.');
        }

        // 移动文件并返回成功响应
        fs.rename(file.path, newPath, (err) => {
            if (err) {
                console.error(err);
                return res.status(500).send('Error moving file.');
            }
            res.status(200).send('File uploaded successfully.');
        });
    });
}

module.exports = uploadFiles;

解释

  • 使用 path.extname 方法获取文件扩展名。
  • 使用 Date.now() 替换 Date.parse,后者返回的是字符串而非时间戳。
  • 在移动文件前,先检查目标目录是否存在,若不存在则创建。
  • 检查文件是否已存在,以防止覆盖已存在的文件。
  • 返回适当的HTTP状态码,以便客户端知道上传结果。

希望这些建议能帮助你改进文件上传处理逻辑。

回到顶部