我编写了一个 Node.js 脚本,该脚本使用 download
、axios
和 fs
模块从由Federal Register ,并下载相关的 PDF 文件。但是,该脚本通常无法下载所有 PDF。
无论出于何种原因,我的脚本在下载所有 PDF 文件之前都会“停止”。意思是,它一开始很好(可能下载 70、80 个文件),但随后就停止了。它不会触发我的 catch block ,也不会以任何方式失败。它只是停止下载。
文件数量根据我使用的 WiFi 连接而有所不同。但是,我始终无法完成代码并触发代码中的 .then
block 。理想情况下,我想在下载文件后使用 .then block 来处理文件。
这是代码:
// The callback function that writes the file...
function writeFile(path, contents, cb){
mkdirp(getDirName(path), function(err){
if (err) return cb(err)
fs.writeFile(path, contents, cb)
})
};
// The function that gets the JSON...
axios.get(`http://federalregister.gov/api/v1/public-inspection-documents.json?conditions%5Bavailable_on%5D=${today}`)
.then(downloadPDFS)
.catch((err) => {
console.log("COULD NOT DOWNLOAD FILES: \n", err);
});
// The function that downloads the data and triggers my write callback...
function downloadPDFS(res) {
const downloadPromises = res.data.results.map(item => (
download(item.pdf_url)
.then(data => new Promise((resolve, reject) => {
writeFile(`${__dirname}/${today}/${item.pdf_file_name}`, data, (err) => {
if(err) reject(err);
else resolve(console.log("FILE WRITTEN: ", item.pdf_file_name));
});
}))
))
return Promise.all(downloadPromises).then((res) => console.log("DONE"))
}
我的项目在Github here ,如果您想安装它并亲自尝试。以下是用简单的英语总结的情况:
该脚本从服务器获取 JSON,其中包含所有 126 个 PDF 的 URL。然后,它将这些 url 的数组传递给同步 map
函数。每个 url 都通过 download
模块转换为一个 promise 。该 Promise 被隐式返回,并存储在 Promise.all
包装器中。当下载 promise 解决时(文档下载完成),我的自定义 writeFile 函数将触发,用下载的数据写入 PDF 文件。下载完所有文件后,Promise.all
包装器应该会解析。但这并没有发生。
出了什么问题?
编辑--
正如您在下面看到的,脚本运行了一段时间,但随后它就停止了并且不再下载任何文件...
最佳答案
如果确实是速率问题,那么有几种方法可以解决它(取决于 API 的速率限制方式)
下面有 3 个解决方案合二为一
rateLimited
...这将触发限制为每秒给定数量的请求的请求
singleQueue
...一次一个请求,没有速率限制,只是系列中的所有请求
multiQueue
...一次最多“进行中”给定数量的请求
const rateLimited = perSecond => {
perSecond = isNaN(perSecond) || perSecond < 0.0001 ? 0.0001 : perSecond;
const milliSeconds = Math.floor(1000 / perSecond);
let promise = Promise.resolve(Date.now);
const add = fn => promise.then(lastRun => {
const wait = Math.max(0, milliSeconds + lastRun - Date.now);
promise = promise.thenWait(wait).then(() => Date.now);
return promise.then(fn);
});
return add;
};
const singleQueue = () => {
let q = Promise.resolve();
return fn => q = q.then(fn);
};
const multiQueue = length => {
length = isNaN(length) || length < 1 ? 1 : length;
const q = Array.from({ length }, () => Promise.resolve());
let index = 0;
const add = fn => {
index = (index + 1) % length;
return q[index] = q[index].then(fn);
};
return add;
};
// uncomment one, and only one, of the three "fixup" lines below
let fixup = rateLimited(10); // 10 per second for example
//let fixup = singleQueue; // one at a time
//let fixup = multiQueue(6); // at most 6 at a time for example
const writeFile = (path, contents) => new Promise((resolve, reject) => {
mkdirp(getDirName(path), err => {
if (err) return reject(err);
fs.writeFile(path, contents, err => {
if (err) return reject(err);
resolve();
})
})
});
axios.get(`http://federalregister.gov/api/v1/public-inspection-documents.json?conditions%5Bavailable_on%5D=${today}`)
.then(downloadPDFS)
.catch((err) => {
console.log("COULD NOT DOWNLOAD FILES: \n", err);
});
function downloadPDFS(res) {
const downloadPromises = res.data.results.map(item => fixup(() =>
download(item.pdf_url)
.then(data => writeFile(`${__dirname}/${today}/${item.pdf_file_name}`, data))
.then(() => console.log("FILE WRITTEN: ", item.pdf_file_name))
));
return Promise.all(downloadPromises).then(() => console.log("DONE"));
}
我还对代码进行了一些重构,因此 downloadPDFS
仅使用 Promise - 所有 Node 回调样式代码都放入 writeFile
关于javascript - Node.js 脚本默默失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52564601/