[英]node.js axios download file stream and writeFile
我想用 axios 下载 pdf 文件并用axios
保存在磁盘(服务器端) fs.writeFile
,我试过:
axios.get('https://xxx/my.pdf', {responseType: 'blob'}).then(response => {
fs.writeFile('/temp/my.pdf', response.data, (err) => {
if (err) throw err;
console.log('The file has been saved!');
});
});
文件已保存,但内容已损坏...
如何正确保存文件?
实际上,我相信之前接受的答案有一些缺陷,因为它不能正确处理写入流,所以如果你在 Axios 给你响应之后调用“then()”,你最终会得到一个部分下载的文件。
当下载稍大的文件时,这是一个更合适的解决方案:
export async function downloadFile(fileUrl: string, outputLocationPath: string) {
const writer = createWriteStream(outputLocationPath);
return Axios({
method: 'get',
url: fileUrl,
responseType: 'stream',
}).then(response => {
//ensure that the user can call `then()` only when the file has
//been downloaded entirely.
return new Promise((resolve, reject) => {
response.data.pipe(writer);
let error = null;
writer.on('error', err => {
error = err;
writer.close();
reject(err);
});
writer.on('close', () => {
if (!error) {
resolve(true);
}
//no need to call the reject here, as it will have been called in the
//'error' stream;
});
});
});
}
这样,您可以调用downloadFile()
,在返回的 Promise 上调用then()
,并确保下载的文件已完成处理。
或者,如果你使用更现代的 NodeJS 版本,你可以试试这个:
import * as stream from 'stream';
import { promisify } from 'util';
const finished = promisify(stream.finished);
export async function downloadFile(fileUrl: string, outputLocationPath: string): Promise<any> {
const writer = createWriteStream(outputLocationPath);
return Axios({
method: 'get',
url: fileUrl,
responseType: 'stream',
}).then(response => {
response.data.pipe(writer);
return finished(writer); //this is a Promise
});
}
您可以简单地使用response.data.pipe
和fs.createWriteStream
将响应通过管道传输到文件
axios({
method: "get",
url: "https://xxx/my.pdf",
responseType: "stream"
}).then(function (response) {
response.data.pipe(fs.createWriteStream("/temp/my.pdf"));
});
文件损坏的问题是由于节点流中的背压。 您可能会发现此链接有助于阅读: https ://nodejs.org/es/docs/guides/backpressuring-in-streams/
我不太喜欢在 JS 代码中使用 Promise 基本声明性对象,因为我觉得它会污染实际的核心逻辑并使代码难以阅读。 最重要的是,您必须提供事件处理程序和侦听器以确保代码完成。
下面给出了与公认答案提出的相同逻辑的更简洁的方法。 它使用流管道的概念。
const util = require('util');
const stream = require('stream');
const pipeline = util.promisify(stream.pipeline);
const downloadFile = async () => {
try {
const request = await axios.get('https://xxx/my.pdf', {
responseType: 'stream',
});
await pipeline(request.data, fs.createWriteStream('/temp/my.pdf'));
console.log('download pdf pipeline successful');
} catch (error) {
console.error('download pdf pipeline failed', error);
}
}
exports.downloadFile = downloadFile
希望这个对你有帮助。
// This works perfectly well!
const axios = require('axios');
axios.get('http://www.sclance.com/pngs/png-file-download/png_file_download_1057991.png', {responseType: "stream"} )
.then(response => {
// Saving file to working directory
response.data.pipe(fs.createWriteStream("todays_picture.png"));
})
.catch(error => {
console.log(error);
});
node fileSystem writeFile
默认将数据编码为 UTF8。 在您的情况下这可能是一个问题。
尝试将您的编码设置为null
并跳过对接收到的数据进行编码:
fs.writeFile('/temp/my.pdf', response.data, {encoding: null}, (err) => {...}
如果您只声明编码而没有其他选项,您也可以将编码标记为字符串(而不是选项对象)。 字符串将作为编码值处理。 像这样:
fs.writeFile('/temp/my.pdf', response.data, 'null', (err) => {...}
我试过了,我确信使用response.data.pipe
和fs.createWriteStream
可以工作。
此外,我想添加我的情况和解决方案
情况:
koa
开发node.js服务器axios
通过 url 获取 pdfpdf-parse
解析 pdf解决方案:
const Koa = require('koa');
const app = new Koa();
const axios = require('axios')
const fs = require("fs")
const pdf = require('pdf-parse');
const utils = require('./utils')
app.listen(process.env.PORT || 3000)
app.use(async (ctx, next) => {
let url = 'https://path/name.pdf'
let resp = await axios({
url: encodeURI(url),
responseType: 'arraybuffer'
})
let data = await pdf(resp.data)
ctx.body = {
phone: utils.getPhone(data.text),
email: utils.getEmail(data.text),
}
})
在这个解决方案中,它不需要写文件和读文件,效率更高。
这对我有用,它还会为图像文件创建一个临时文件,以防未指定输出文件路径:
const fs = require('fs')
const axios = require('axios').default
const tmp = require('tmp');
const downloadFile = async (fileUrl, outputLocationPath) => {
if(!outputLocationPath) {
outputLocationPath = tmp.fileSync({ mode: 0o644, prefix: 'kuzzle-listener-', postfix: '.jpg' });
}
let path = typeof outputLocationPath === 'object' ? outputLocationPath.name : outputLocationPath
const writer = fs.createWriteStream(path)
const response = await axios.get(fileUrl, { responseType: 'arraybuffer' })
return new Promise((resolve, reject) => {
if(response.data instanceof Buffer) {
writer.write(response.data)
resolve(outputLocationPath.name)
} else {
response.data.pipe(writer)
let error = null
writer.on('error', err => {
error = err
writer.close()
reject(err)
})
writer.on('close', () => {
if (!error) {
resolve(outputLocationPath.name)
}
})
}
})
}
这是一个非常简单的 Jest 测试:
it('when downloadFile should downloaded', () => {
downloadFile('https://i.ytimg.com/vi/HhpbzPMCKDc/hq720.jpg').then((file) => {
console.log('file', file)
expect(file).toBeTruthy()
expect(file.length).toBeGreaterThan(10)
})
})
const res = await axios.get(url, { responseType: 'arraybuffer' });
fs.writeFileSync(downloadDestination, res.data);
Lorenzo 的回答可能是最好的答案,因为它使用的是内置的 axios。 如果您只想要缓冲区,这是一种简单的方法:
const downloadFile = url => axios({ url, responseType: 'stream' })
.then(({ data }) => {
const buff = []
data.on('data', chunk => buff.push(chunk))
return new Promise((resolve, reject) => {
data.on('error', reject)
data.on('close', () => resolve(Buffer.concat(buff)))
})
})
// better
const downloadFile = url => axios({ url, responseType: 'arraybuffer' }).then(res => res.data)
const res = await downloadFile(url)
fs.writeFileSync(downloadDestination, res)
我仍然可能使用'arraybuffer' responseType
如果你只想要文件使用这个
const media_data =await axios({url: url, method: "get", responseType: "arraybuffer"})
writeFile("./image.jpg", Buffer.from(media_data.data), {encoding: "binary"}, console.log)
有一种更简单的方法可以用几行来完成:
const fileResponse = await axios({
url: fileUrl,
method: "GET",
responseType: "stream",
});
// Write file to disk (here I use fs.promise but you can use writeFileSync it's equal
await fsPromises.writeFile(filePath, fileResponse.data);
Axios
具有处理streams
的内部能力,您不必为此干预低级节点 API。
查看https://axios-http.com/docs/req_config (在文档中找到您可以使用的所有类型的responseType
部分)。
import download from "downloadjs";
export const downloadFile = async (fileName) => {
axios({
method: "get",
url: `/api/v1/users/resume/${fileName}`,
responseType: "blob",
}).then(function (response) {
download(response.data, fileName);
});
};
这对我来说很好
这是我使用节点 js 运行的示例代码有一个同步税错误
应该是writeFile而不是WriteFile
const axios = require('axios');
const fs = require('fs');
axios.get('http://www.africau.edu/images/default/sample.pdf', {responseType: 'blob'}).then(response => {
fs.writeFile('./my.pdf', response.data, (err) => {
if (err) throw err;
console.log('The file has been saved!');
});
});
保存文件后,它可能看起来像在文本编辑器中,但文件已正确保存
%PDF-1.3
%����
1 0 obj
<<
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj
2 0 obj
<<
/Type /Outlines
/Count 0
>>
endobj
3 0 obj
<<
/Type /Pages
/Count 2
/Kids [ 4 0 R 6 0 R ]
>>
endobj
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.