我正在服务器端使用post方法来检索请求目录内的所有文件(不是递归的),下面是我的代码。
我在不使用res.json(pathContent);
的情况下用更新的pathContent
发回响应(setTimeout
)遇到困难。
我知道这是由于所使用的文件系统方法(readdir
和stat
)的异步行为引起的,并且需要使用某种回调,异步或promise技术。
我尝试使用async.waterfall
和readdir
的整个正文作为一个函数,而将res.json(pathContent)
用作另一个函数,但是它没有将更新后的数组发送到客户端
我知道关于此异步操作有成千上万的问题,但是在阅读了许多帖子后却找不到解决方法。
任何评论将不胜感激。谢谢。
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');
var pathName = '';
const pathContent = [];
app.post('/api/files', (req, res) => {
const newPath = req.body.path;
fs.readdir(newPath, (err, files) => {
if (err) {
res.status(422).json({ message: `${err}` });
return;
}
// set the pathName and empty pathContent
pathName = newPath;
pathContent.length = 0;
// iterate each file
const absPath = path.resolve(pathName);
files.forEach(file => {
// get file info and store in pathContent
fs.stat(absPath + '/' + file, (err, stats) => {
if (err) {
console.log(`${err}`);
return;
}
if (stats.isFile()) {
pathContent.push({
path: pathName,
name: file.substring(0, file.lastIndexOf('.')),
type: file.substring(file.lastIndexOf('.') + 1).concat(' File'),
})
} else if (stats.isDirectory()) {
pathContent.push({
path: pathName,
name: file,
type: 'Directory',
});
}
});
});
});
setTimeout(() => { res.json(pathContent); }, 100);
});
答案 0 :(得分:3)
最简单,最干净的方法是使用await
/ async
,这样您就可以利用Promise,并且代码几乎就像同步代码一样。
为此,您需要readdir
核心库的stat
创建的promisify
和utils
的简化版本。
const { promisify } = require('util')
const readdir = promisify(require('fs').readdir)
const stat = promisify(require('fs').stat)
async function getPathContent(newPath) {
// move pathContent otherwise can have conflicts with concurrent requests
const pathContent = [];
let files = await readdir(newPath)
let pathName = newPath;
// pathContent.length = 0; // not needed anymore because pathContent is new for each request
const absPath = path.resolve(pathName);
// iterate each file
// replace forEach with (for ... of) because this makes it easier
// to work with "async"
// otherwise you would need to use files.map and Promise.all
for (let file of files) {
// get file info and store in pathContent
try {
let stats = await stat(absPath + '/' + file)
if (stats.isFile()) {
pathContent.push({
path: pathName,
name: file.substring(0, file.lastIndexOf('.')),
type: file.substring(file.lastIndexOf('.') + 1).concat(' File'),
})
} else if (stats.isDirectory()) {
pathContent.push({
path: pathName,
name: file,
type: 'Directory',
});
}
} catch (err) {
console.log(`${err}`);
}
}
return pathContent;
}
app.post('/api/files', (req, res, next) => {
const newPath = req.body.path;
getPathContent(newPath).then((pathContent) => {
res.json(pathContent);
}, (err) => {
res.status(422).json({
message: `${err}`
});
})
})
并且您不应该使用+
(absPath + '/' + file
)连接路径,而应使用path.join(absPath, file)
或path.resolve(absPath, file)
。
并且永远不要以为请求执行的代码,像var pathName = '';
和const pathContent = [];
这样的全局变量为基础来编写代码。这可能在您的测试环境中有效,但是肯定会导致生产中出现问题。其中两个请求在“相同时间”
答案 1 :(得分:0)
以下是一些选择:
Sync
结尾)。速度较慢,但代码更改相当简单,而且非常易于理解。util.promisify
)为每个统计信息创建一个promise,并使用Promise.all
等待所有统计信息完成。之后,您可以使用异步函数并等待,以便于阅读代码和简化错误处理。 (可能是最大的代码更改,但它将使异步代码更易于遵循)res.json
表单(代码更改最少,但容易出错)答案 2 :(得分:0)
答案 3 :(得分:0)
根据我收到的初始评论和参考,我改用了readdirSync和statSync并使它能够正常工作。我还将查看其他答案,并了解实现此问题的其他方法。
感谢大家的投入。
这是我的解决方法。
const express = require('express');
const bodyParser = require('body-parser');
const fs = require('fs');
const path = require('path');
var pathName = '';
const pathContent = [];
app.post('/api/files', (req, res) => {
const newPath = req.body.path;
// validate path
let files;
try {
files = fs.readdirSync(newPath);
} catch (err) {
res.status(422).json({ message: `${err}` });
return;
}
// set the pathName and empty pathContent
pathName = newPath;
pathContent.length = 0;
// iterate each file
let absPath = path.resolve(pathName);
files.forEach(file => {
// get file info and store in pathContent
let fileStat = fs.statSync(absPath + '/' + file);
if (fileStat.isFile()) {
pathContent.push({
path: pathName,
name: file.substring(0, file.lastIndexOf('.')),
type: file.substring(file.lastIndexOf('.') + 1).concat(' File'),
})
} else if (fileStat.isDirectory()) {
pathContent.push({
path: pathName,
name: file,
type: 'Directory',
});
}
});
res.json(pathContent);
});