17
votes

node.js axios télécharger le flux de fichiers et writeFile

je veux télécharger un fichier pdf avec axios et enregistrer sur le disque (côté serveur) avec fs.writeFile , j'ai essayé:

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!');
    });
});

le fichier est enregistré mais le contenu est cassé ...

comment enregistrer correctement le fichier?


4 commentaires

vous obtenez le journal de la console "Le fichier a été enregistré" et le fichier est créé et juste le contenu est faux?


où vous appelez axios.get? il n'attendra pas que le fichier soit écrit. mieux vaut promettre le fs ou utiliser fs-extra ou utiliser les méthodes promises de fs. et utilisez comme return fs.writeFile (...)


@RolandStarke oui, le fichier est enregistré


J'ai publié une approche plus propre pour résoudre le problème à l'aide de pipelines de flux de nœuds ci-dessous. C'est sur le même concept que propose la réponse acceptée. stackoverflow.com/a/64925465/3476378


8 Réponses :


-2
votes

Ceci est mon exemple de code exécuté avec le nœud js Il y a une erreur de synctaxe

doit être writeFile et non WriteFile

%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

Une fois le fichier enregistré, il peut ressembler à un éditeur de texte, mais le fichier a été enregistré correctement

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!');
    });
});


1 commentaires

Comment est-ce une réponse? Vous n'avez rien expliqué de différent de ce que le PO a publié.



1
votes

node fileSystem writeFile encode les données par défaut en UTF8. ce qui pourrait être un problème dans votre cas.

Essayez de définir votre encodage sur null et ignorez l'encodage des données reçues:

fs.writeFile('/temp/my.pdf', response.data, 'null', (err) => {...}

vous pouvez également décaler l'encodage en tant que chaîne (au lieu de l'objet d'options) si vous ne déclarez que l'encodage et aucune autre option. string sera traité comme valeur d'encodage. En tant que tel:

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

lire plus dans fileSystem API write_file


2 commentaires

Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables.


@ double bip tnx pour votre commentaire. J'ai édité avec quelques explications et lu du matériel à partir de l' node fileSystem API sur la fonction writeFile. :)



39
votes

Vous pouvez simplement utiliser response.data.pipe et fs.createWriteStream pour fs.createWriteStream réponse vers le fichier

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


3 commentaires

Merci beaucoup!! Je cherchais ça pour toujours


Cette réponse n'est pas complète, car lorsque vous téléchargez des fichiers plus volumineux, le tube vous donnera plus d'un événement. Ce code n'attend pas que tout le fichier ait été téléchargé avant de pouvoir l'appeler then . Jetez un œil à ma solution pour trouver ce que je considère comme une solution plus complète.


response.data.pipe n'est pas une fonction



2
votes

J'ai essayé, et je suis sûr que l'utilisation de response.data.pipe et fs.createWriteStream peut fonctionner.


En plus, je veux ajouter ma situation et ma solution

Situation:

  • utiliser koa pour développer un serveur node.js
  • utiliser axios pour obtenir un pdf via url
  • en utilisant pdf-parse pour analyser le pdf
  • extraire des informations de pdf et les renvoyer en tant que json au navigateur

Solution:

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),
        }
})

Dans cette solution, il n'est pas nécessaire d'écrire et de lire un fichier, c'est plus efficace.


0 commentaires

6
votes
// 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);  
});  

1 commentaires

Bienvenue dans StackOverflow! Vous voudrez peut-être fournir quelques explications pour accompagner votre exemple de code.



38
votes

En fait, je pense que la réponse précédemment acceptée a quelques défauts, car elle ne gérera pas correctement le flux d'écriture, donc si vous appelez "then ()" après qu'Axios vous a donné la réponse, vous finirez par avoir un fichier partiellement téléchargé.

C'est une solution plus appropriée lors du téléchargement de fichiers légèrement plus volumineux:

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;
      });
    });
  });
}

De cette façon, vous pouvez appeler downloadFile() , appeler then() sur la promesse retournée et vous assurer que le fichier téléchargé aura terminé le traitement.


3 commentaires

Ceci est correct et résout exactement le problème lié à l'erreur de données partielle


cela devrait être la réponse acceptée. il a corrigé l'erreur de téléchargement partiel


J'ai publié une approche plus propre sur le même concept d'utilisation de pipelines de flux ci-dessous: stackoverflow.com/a/64925465/3476378 .



1
votes
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);
    });
};
it's work fine to me

0 commentaires

1
votes

Le problème avec un fichier cassé est dû à la contre- pression dans les flux de nœuds . Vous pouvez trouver ce lien utile à lire: https://nodejs.org/es/docs/guides/backpressuring-in-streams/

Je ne suis pas vraiment fan de l'utilisation des objets déclaratifs de base Promise dans les codes JS car je pense que cela pollue la logique de base réelle et rend le code difficile à lire. En plus de cela, vous devez provisionner des gestionnaires d'événements et des écouteurs pour vous assurer que le code est terminé.

Une approche plus propre sur la même logique que celle proposée par la réponse acceptée est donnée ci-dessous. Il utilise les concepts de pipelines de flux .

const util = require("util");
const stream = require("stream");
const pipeline = util.promisify(stream.pipeline);

const downloadFile = async () => {
    try {
        await pipeline( axios.get('https://xxx/my.pdf', {responseType: 'blob'}), 
                            fs.createWriteStream("/temp/my.pdf"));
        console.log("donwload pdf pipeline successfull");   
    } catch (error) {
        console.error("donwload pdf pipeline failed", error);
    }
}

exports.downloadFile = downloadFile

J'espère que vous trouvez ça utile.


0 commentaires