node.js axios下载文件并写入File

ar0*_*968 1 javascript node.js 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!');
    });
});
Run Code Online (Sandbox Code Playgroud)

文件已保存,但内容已损坏...

如何正确保存文件?

cso*_*iou 66

实际上,我认为之前接受的答案存在一些缺陷,因为它无法正确处理写入流,因此如果在 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;
      });
    });
  });
}
Run Code Online (Sandbox Code Playgroud)

这样,您可以调用downloadFile(),调用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(async response => {
    response.data.pipe(writer);
    return finished(writer); //this is a Promise
  });
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是公认的答案。它修复了部分下载错误 (2认同)
  • 我在下面发布了一种与使用流管道相同概念的更简洁的方法:/sf/answers/4544782581/。 (2认同)
  • 我不确定我是否遵循。当字节被下载时,它们被流式传输到文件中,一旦所有字节都被流式传输,Promise 就结束,应用程序流的其余部分继续。示例中的“then”在文件下载完成之前被调用 - 检查有关 axios 的“stream”responseType 的文档。 (2认同)
  • response.data.pipe 不是一个函数 (2认同)

use*_*378 34

文件损坏的问题是由于节点流中的背压造成的。您可能会发现此链接对阅读很有用: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
Run Code Online (Sandbox Code Playgroud)

希望这个对你有帮助。


小智 14

以下代码取自https://gist.github.com/senthilmpro/072f5e69bdef4baffc8442c7e696f4eb?permalink_comment_id=3620639#gistcomment-3620639对我有用

const res = await axios.get(url, { responseType: 'arraybuffer' });
fs.writeFileSync(downloadDestination, res.data);
Run Code Online (Sandbox Code Playgroud)


pon*_*tek 13

您可以简单地使用response.data.pipefs.createWriteStream传递响应到文件

axios({
    method: "get",
    url: "https://xxx/my.pdf",
    responseType: "stream"
}).then(function (response) {
    response.data.pipe(fs.createWriteStream("/temp/my.pdf"));
});
Run Code Online (Sandbox Code Playgroud)

  • response.data.pipe 不是一个函数 (11认同)
  • 要批评此解决方案,您**必须**将responseType 设置为“stream”。当您尝试将其通过管道传输到另一个流时,不这样做会导致错误。 (2认同)

Arm*_*and 8

// 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);  
});  
Run Code Online (Sandbox Code Playgroud)

  • 欢迎来到 StackOverflow!您可能需要提供一些解释来配合您的代码示例。 (6认同)
  • 这不会正常工作,因为它不会等到文件下载完成后再继续承诺链。 (2认同)

Fed*_*eSc 6

节点文件系统writeFile默认将数据编码为 UTF8。这对你来说可能是个问题。

尝试将编码设置为null并跳过对接收​​到的数据进行编码:

fs.writeFile('/temp/my.pdf', response.data, {encoding: null}, (err) => {...}
Run Code Online (Sandbox Code Playgroud)

如果您只声明编码而没有其他选项,您还可以将编码标记为字符串(而不是选项对象)。字符串将作为编码值处理。像这样:

fs.writeFile('/temp/my.pdf', response.data, 'null', (err) => {...}
Run Code Online (Sandbox Code Playgroud)

更多内容请阅读fileSystem API write_file


Mat*_*ulo 5

有一种更简单的方法,只需几行即可完成:

import fs from 'fs';

const fsPromises = fs.promises;

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);
Run Code Online (Sandbox Code Playgroud)

Axios具有内部处理能力streams,您不必为此干预低级 Node API。

查看https://axios-http.com/docs/req_configresponseType在文档中查找您可以使用的所有类型的部分)。