我创建了一个脚本来将文件从gridfs迁移到新的本地C:驱动器,然后重命名该文件,因为我无法使用带有writeFileSync的变量来动态命名包含扩展名的文件。
我遇到的问题是,当我在位置路径变量中使用任何变量时,它将返回错误,没有这样的文件或目录。当我显式写出字符串并且不使用变量时,它会毫无问题地找到它并正确重命名文件。相同的路径,只是创建路径的两种不同方法。可能是由 javascript 的按引用传递和按值传递引起的问题?目前不知道。
我正在通过控制台记录所有路径进行调试,因为我去确认最终目标字符串是正确的,并且已经验证了在使用引用的变量时它与专门编写为单个字符串时完全相同。
fs.writeFileSync(__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/tempfile`, data);
unprocessedPath1 = (__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/tempfile`); //DOES NOT WORK
unprocessedPath2 = (__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/${tempFile.filename}`); //DOES NOT WORK
//RETURNS: C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile
//RETURNS: C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile1
// unprocessedPath1 = `C:/Sites/CRM/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/tempfile`; // WORKS
// unprocessedPath2 = `C:/Sites/CRM/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/tempfile1`; // WORKS
//RETURNS: C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile
//RETURNS: C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile1
var correctPath1 = path.normalize(unprocessedPath1);
var correctPath2 = path.normalize(unprocessedPath2);
fs.renameSync(correctPath1, correctPath2, function(err) {
if ( err ) console.log('RENAME ERROR: ' + err);
});
RENAME ERROR: Error: ENOENT: no such file or directory, rename 'C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile' -> 'C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f602019-08-16T10:50.wav'
编辑:在尝试重命名之前确认目标存在文件。在重命名之前添加了console.log(fs.existsSync(correctPath1))
行。
true
RENAME ERROR: Error: ENOENT: no such file or directory, rename 'C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile' -> 'C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f602019-08-16T10:50.wav'
编辑:这是整个请求。
//DOWNLOAD GRIDFS DATABASE'S OLD FILES AND SAVE TO LOCAL DRIVE
router.get('/export/gridfs', middleware.isLoggedIn, (req, res) => {
gfs = Grid(conn.db, mongoose.mongo);
gfs.collection('uploads').find().toArray((err, files) => {
console.log("OPENING FILES OBJECT: ");
console.log(util.inspect(files, false, null, true /* enable colors */));
files.forEach(file => {
console.log("FOR EACH FILE");
tempFile = file;
console.log("CREATING FILE PRE SAVE");
File.create(tempFile, function(err, file){
console.log("FILE PRE CREATED");
if(err){
console.log("ERROR OCCURED: " + err);
console.log("SKIPPING FILE");
} else {
console.log("NO ERROR");
console.log(util.inspect(file, false, null, true /* enable colors */))
console.log(util.inspect(tempFile, false, null, true /* enable colors */))
console.log("MAKING NEW FOLDER IN LEAD ID USING FILE ID");
console.log('public/uploads/' + tempFile.metadata.parent +"/"+ file._id);
mkdirp('public/uploads/' + tempFile.metadata.parent +"/"+ file._id, function() {});
console.log("FOLDER CREATED");
console.log("File Before");
console.log(file);
console.log("OPENING file OBJECT: ");
console.log(util.inspect(file, {showHidden: false, depth: null}))
console.log("OPENING tempfile OBJECT: ");
console.log(util.inspect(tempFile, false, null, true /* enable colors */))
file.filename = tempFile.filename;
file.contentType = tempFile.mimetype;
file.fileLocation = `/public/uploads/${tempFile.metadata.parent}/${file._id}/${tempFile.filename}`;
file.metadata = { parent: tempFile.metadata.parent };
file.createdAt = tempFile.uploadDate;
console.log("filename: " + file.filename);
console.log("contentType" + file.contentType);
console.log("fileLocation" + file.fileLocation);
console.log("metadata: " + file.metadata);
console.log("File After");
console.log("FILE: " + file);
console.log("tempFile: " + tempFile);
console.log(tempFile.newFileName);
console.log(tempFile.originalname);
console.log(tempFile.mimetype);
console.log(tempFile.contentType);
console.log("File After");
console.log(file);
//save note
file.save();
console.log("File Saved");
const tempId = file._id;
console.log("tempId: " + tempId);
console.log("OPENING tempId OBJECT: ");
console.log(util.inspect(tempId, false, null, true /* enable colors */));
console.log("PROCESSING FILE");
console.log(util.inspect(file, false, null, true /* enable colors */));
console.log("Starting gridfs stream");
gfs.files.find({ _id: new ObjectId(file._id) }, (err, file) => {
// Check if file
console.log("CHECKING FOR FILE ENTRY");
if (!file || file.length === 0) {
return res.status(404).json({
err: 'No file exists'
});
}
console.log("FILE ENTRY FOUND");
let data = [];
let readstream = gfs.createReadStream({
filename: tempFile.filename
});
console.log("Creating read stream");
readstream.on('data', function(chunk) {
console.log("PUSHING CHUNK");
console.log(chunk);
data.push(chunk);
console.log("PUSHED CHUNK");
});
readstream.on('end', function() {
console.log("ENDING STREAM");
data = Buffer.concat(data);
console.log("WRITING TO LOCAL DRIVE");
var fileExt = path.extname(tempFile.filename);
console.log(fileExt)
fs.writeFileSync(__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/tempfile`, data);
console.log("RETURNING FILE TO CLIENT");
console.log("RENAMING FILE AT LOCATION WITH EXTENSION");
var unprocessedPath1 = (__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/tempfile`);
var unprocessedPath1String = `C:/Sites/CRM/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/tempfile`; //works
console.log("Path1: " + unprocessedPath1);
console.log("Path1String: " + unprocessedPath1String);
var unprocessedPath2 = (__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/${tempFile.filename}`);
// unprocessedPath2 = `C:/Sites/CRM/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/tempfile1`; //works
console.log("Path2: " + unprocessedPath2);
var correctPath1 = path.normalize(unprocessedPath1);
console.log("NORMALIZED Path1: "+ correctPath1);
var correctPath2 = path.normalize(unprocessedPath2);
console.log("NORMALIZED Path2: " + correctPath2);
// correctPath1 = String(correctPath1);
// correctPath2 = String(correctPath2);
console.log(fs.existsSync(correctPath1))
console.log(fs.existsSync(unprocessedPath1String))
fs.rename(correctPath1, correctPath2, function(err) {
if ( err ) console.log('RENAME ERROR: ' + err);
console.log("RENAME COMPLETE");
});
});
readstream.on('error', function(err) {
console.log('An error occured!', err);
throw err;
});
res.send("EXPORTED");
});
}
});
});
});
});
编辑:这是日志。
OPENING FILES OBJECT:
[ { _id: 5d56ece48f6b3b09f0068f60,
length: 221228,
chunkSize: 261120,
uploadDate: 2019-08-16T17:50:30.212Z,
filename: '2019-08-16T10:50.wav',
md5: '47fbec41801f73efc53d7e8f73b4e596',
contentType: 'audio/wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' } } ]
FOR EACH FILE
CREATING FILE PRE SAVE
FILE PRE CREATED
NO ERROR
{ _id: 5d56ece48f6b3b09f0068f60,
filename: '2019-08-16T10:50.wav',
contentType: 'audio/wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' },
createdAt: 2019-08-22T02:50:55.594Z,
__v: 0 }
{ _id: 5d56ece48f6b3b09f0068f60,
length: 221228,
chunkSize: 261120,
uploadDate: 2019-08-16T17:50:30.212Z,
filename: '2019-08-16T10:50.wav',
md5: '47fbec41801f73efc53d7e8f73b4e596',
contentType: 'audio/wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' } }
MAKING NEW FOLDER IN LEAD ID USING FILE ID
public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60
FOLDER CREATED
File Before
{ _id: 5d56ece48f6b3b09f0068f60,
filename: '2019-08-16T10:50.wav',
contentType: 'audio/wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' },
createdAt: 2019-08-22T02:50:55.594Z,
__v: 0 }
OPENING file OBJECT:
{ _id: 5d56ece48f6b3b09f0068f60,
filename: '2019-08-16T10:50.wav',
contentType: 'audio/wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' },
createdAt: 2019-08-22T02:50:55.594Z,
__v: 0 }
OPENING tempfile OBJECT:
{ _id: 5d56ece48f6b3b09f0068f60,
length: 221228,
chunkSize: 261120,
uploadDate: 2019-08-16T17:50:30.212Z,
filename: '2019-08-16T10:50.wav',
md5: '47fbec41801f73efc53d7e8f73b4e596',
contentType: 'audio/wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' } }
filename: 2019-08-16T10:50.wav
contentTypeundefined
fileLocation/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/2019-08-16T10:50.wav
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' }
File After
FILE: { _id: 5d56ece48f6b3b09f0068f60,
filename: '2019-08-16T10:50.wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' },
createdAt: 2019-08-16T17:50:30.212Z,
__v: 0,
fileLocation: '/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/2019-08-16T10:50.wav' }
tempFile: [object Object]
undefined
undefined
undefined
audio/wav
File After
{ _id: 5d56ece48f6b3b09f0068f60,
filename: '2019-08-16T10:50.wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' },
createdAt: 2019-08-16T17:50:30.212Z,
__v: 0,
fileLocation: '/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/2019-08-16T10:50.wav' }
File Saved
tempId: 5d56ece48f6b3b09f0068f60
OPENING tempId OBJECT:
5d56ece48f6b3b09f0068f60
PROCESSING FILE
{ _id: 5d56ece48f6b3b09f0068f60,
filename: '2019-08-16T10:50.wav',
metadata: { parent: '5d56ebd88f6b3b09f0068f5c' },
createdAt: 2019-08-16T17:50:30.212Z,
__v: 0,
fileLocation: '/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/2019-08-16T10:50.wav' }
Starting gridfs stream
CHECKING FOR FILE ENTRY
FILE ENTRY FOUND
Creating read stream
(node:3008) DeprecationWarning: GridStore is deprecated, and will be removed in a future version. Please use GridFSBucket instead
PUSHING CHUNK
<Buffer 52 49 46 46 24 60 03 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 01 00 80 bb 00 00 00 ee 02 00 02 00 10 00 64 61 74 61 00 60 03 00 00 00 00 00 00 00 ... >
PUSHED CHUNK
(node:3008) DeprecationWarning: GridStore is deprecated, and will be removed in a future version. Please use GridFSBucket instead
PUSHING CHUNK
<Buffer 52 49 46 46 24 60 03 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 01 00 80 bb 00 00 00 ee 02 00 02 00 10 00 64 61 74 61 00 60 03 00 00 00 00 00 00 00 ... >
PUSHED CHUNK
PUSHING CHUNK
<Buffer 52 49 46 46 24 60 03 00 57 41 56 45 66 6d 74 20 10 00 00 00 01 00 01 00 80 bb 00 00 00 ee 02 00 02 00 10 00 64 61 74 61 00 60 03 00 00 00 00 00 00 00 ... >
PUSHED CHUNK
ENDING STREAM
WRITING TO LOCAL DRIVE
.wav
RETURNING FILE TO CLIENT
RENAMING FILE AT LOCATION WITH EXTENSION
Path1: C:SitesCRMroutes/../public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/tempfile
Path1String: C:/Sites/CRM/public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/tempfile
Path2: C:SitesCRMroutes/../public/uploads/5d56ebd88f6b3b09f0068f5c/5d56ece48f6b3b09f0068f60/2019-08-16T10:50.wav
NORMALIZED Path1: C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile
NORMALIZED Path2: C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f602019-08-16T10:50.wav
true
true
RENAME ERROR: Error: ENOENT: no such file or directory, rename 'C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f60tempfile' -> 'C:SitesCRMpublicuploads5d56ebd88f6b3b09f0068f5c5d56ece48f6b3b09f0068f602019-08-16T10:50.wav'
RENAME COMPLETE
刚刚看到您添加的新代码。
在我看来,这就像使用异步操作的循环中的共享变量。
你的循环将与自身发生冲突。.forEach()
不会等待异步操作完成,因此您将运行循环的多次迭代并尝试使用相同的变量。 您可以通过停止循环中操作的并行运行或使用let
非常仔细地定义变量来修复,以便它们不受循环的每次迭代的影响,并且不使用任何修改过的共享变量。
分配给变量时出现问题的原因是该变量位于共享范围内,并且循环的所有迭代都尝试使用相同的变量,其中一些变量相互冲突。
在.forEach()
内部修改的所有变量都需要在内部向.forEach()
声明,以便它们对于循环的每次迭代都是唯一且独立的。 它们都不应在更高的范围内声明。
变量tempFile
是问题的一部分。 在某些异步操作尝试使用它之前,它将被.forEach()
循环的后续迭代覆盖。 您有如此多的嵌套异步操作,我没有研究异步回调中使用的每个变量,以查看其他哪些变量可能有相同的问题。
因此,您执行此操作的要点:
var unprocessedPath1 = (__dirname + `/../public/uploads/${tempFile.metadata.parent}/${tempId}/tempfile`);
tempfile
很可能已被循环的下一次迭代覆盖,因为这发生在深度嵌套的异步回调中,该回调将在.forEach()
循环已经完成并且其他值可能已写入tempfile
后被调用。
仅供参考,您根本没有显示tempFile
变量的声明,因此可能是在更高的范围内声明的或意外的模块级变量。 只是将其声明为:
let tempFile = file;
在.forEach()
的顶部,回调将为循环的每个调用提供该变量自己的副本,并至少修复第一个问题。
作为问题的简化示例,请运行以下代码片段:
const data = [1,2,3,4];
const base = "base";
let tempFile;
data.forEach(function(num) {
tempFile = base + num;
setTimeout(function() {
console.log(tempFile);
}, 1);
});
为了简单起见,我在这里使用setTimeout()
作为简单的异步回调,但问题在您的代码中是相同的,因为您在每次调用循环时都分配给tempFile
,然后在异步回调中引用它。
console.log(tempFile);
不显示所需的值,因为.forEach()
循环在调用单个异步回调(节点.js事件循环的乘积以及异步操作如何使用它(之前运行完成。
然而,如果将tempFile
声明移动到循环本身中,则循环的每个迭代都有一个唯一的该变量的单独副本,您将获得所需的输出:
const data = [1,2,3,4];
const base = "base";
data.forEach(function(num) {
let tempFile = base + num;
setTimeout(function() {
console.log(tempFile);
}, 1);
});