上传写入磁盘一:启动Node.js服务启动一个Node.js服务,指定路由/upload/image收到请求后,调用uploadImageHandler方法并传入Request对象。consthttp=require('http');constformidable=require('formidable');constfs=require('fs');constfsPromises=fs.promises;constpath=require('path');constPORT=process.env.PORT||3000;constserver=http.createServer(async(req,res)=>{if(req.url==='/upload/image'&&req.method.toLocaleLowerCase()==='post'){uploadImageHandler(req,res);}else{res.setHeader('statusCode',404);res.end('Notfound!')}});server.listen(PORT,()=>{console.log(`serverislisteningat${server.address().port}`);});二:Imageprocessingobjectformidable是一个npm模块,用于处理上传的文件、图片等数据,form.parse是回调转换成Promise方便处理。Tips:拼接路径时,使用路径模块的join方法,会将我们传入的多个路径参数进行拼接,因为linux和windows等不同系统使用的符号不同,该方法会根据系统本身。constuploadImageHandler=async(req,res)=>{constform=newformidable.IncomingForm({multiples:true});form.encoding='utf-8';form.maxFieldsSize=1024*5;form.keepExtensions=true;try{const{file}=awaitnewPromise((resolve,reject)=>{form.parse(req,(err,fields,file)=>{if(err){returnreject(err);}returnresolve({fields,file});});});const{name:filename,path:sourcePath}=file.img;constdestPath=path.join(__dirname,filename);console.log(`sourcePath:${sourcePath}.destPath:${destPath}`);awaitmv(sourcePath,destPath);console.log(`File${filename}writesuccess.`);res.writeHead(200,{'Content-Type':'application/json'});res.结束(JSON.stringify({code:'SUCCESS',message:`Uploadsuccess.`}));}catch(err){console.error(`Movefilefailedwithmessage:${err.message}`);res.writeHead(200,{'Content-Type':'application/json'});res.end(JSON.stringify({code:'ERROR',message:`${err.message}`}));}}三:实现mv方法fs.rename重命名文件并将上传的图像写入本地目标路径。一种简单的方法是使用fs模块的rename(sourcePath,destPath)方法,它异步地要重命名sourcePath文件,请使用以下命令:constmv=async(sourcePath,destPath)=>{returnfsPromises.rename(sourcePath,destPath);};使用fs.rename()时也使用cross-devicelinknotpermitted()请注意cross-devicelinknotpermitted错误,请参阅rename(2)—Linux手册页:**EXDEV**oldpath和newpath不在相同的挂载文件系统。(Linux允许一个文件系统挂载在多个点,但是rename()不能跨不同的挂载点工作,即使相同的文件系统挂载在两个点上。)oldPath和newPath不在同一个挂载的文件系统上(Linux允许一个文件系统tobemounttomultiplepoints,butrename()cannotWorkacrossdifferentmountpoints,evenifthesamefilesystemwasmountedonthebothmountpoints.)这个问题在Windows系统上也遇到,参考http://errorco.de/win32/winerror-h/error_not_same_device/0x80070011/winerror.h0x80070011#defineERROR_NOT_SAME_DEVICE系统无法将文件移动到不同的磁盘驱动器。(系统无法将文件移动到不同的磁盘驱动器。)这里转载于Windows,因为使用formidable上传文件时,默认目录是操作系统os.tmpdir()的默认目录,对应我电脑的C盘。当我用fs.rename()重命名到F盘时,出现如下错误:C:\Users\ADMINI~1\AppData\Local\Temp\upload_3cc33e9403930347b89ea47e4045b940F:\study\test\202366[Error:EXDEV:cross-devicelinknotpermitted,重命名'C:\Users\ADMINI~1\AppData\Local\Temp\upload_3cc33e9403930347b89ea47e4045b940'->'F:\study\test\202366']{errno:-4037,code:'EXDEV',syscall:'rename',path:'C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\upload_3cc33e9403930347b89ea47e4045b940',dest:'F:\\study\\test\\202366'}设置源路径和目标路径要一致磁盘分区设置上传文件中间件的临时路径为文件最终写入的磁盘分区。比如我们在Windows测试时将图片保存在F盘,所以将强大的form对象的uploadDir属性设置为F盘,如下所示:constform=newformidable.IncomingForm({multiples:true});form。uploadDir='F:\\'form.parse(req,(err,fields,file)=>{...});这种方法有一定的局限性,如果写入位置在不同的磁盘空间怎么办?你可以看看下面的方法。读写删除临时文件一种可能的方法是读取临时文件并写入新位置,然后在最后删除临时文件。所以下面的代码创建了一个可读流和一个可写流对象,使用pipe将数据写入新位置,最后调用fs模块的unlink方法删除临时文件。constmv=async(sourcePath,destPath)=>{try{awaitfsPromises.rename(sourcePath,destPath);}catch(error){if(error.code==='EXDEV'){constreadStream=fs.createReadStream(sourcePath);constwriteStream=fs.createWriteStream(destPath);returnnewPromise((resolve,reject)=>{readStream.pipe(writeStream);readStream.on('end',onClose);readStream.on('error',onError);asyncfunctiononClose(){awaitfsPromises.unlink(sourcePath);resolve();}functiononError(err){console.error(`Filewritefailedwithmessage:${err.message}`);writeStream.close();reject(err)}})}throwerror;}}四:测试方法一:终端调用curl--location--requestPOST'localhost:3000/upload/image'\--form'img=@/Users/Downloads/Mayuejun.jpeg'方法二:POSTMAN调用参考https://github.com/andrewrk/node-mv/blob/master/index.jshttps://stackoverflow.com/questions/43206198/what-does-the-exdev-cross-device-link-not-permitted-error-mean/43206506#43206506https://nodejs.org/api/fs.html#fs_fs_rename_oldpath_newpath_callback本文转载自微信公众号》Nodejs技术栈》,可以通过以下二维码关注和转载本文,请联系Nodejs技术栈公众号。
