Getting incomplete files when using Azure Functions



  • When I was testing the below on local (and calling a local jsreport), everything was working fine.

    const axios = require("axios");
    const express = require("express");
    const router = express.Router();
    const fs = require("fs");
    const apiHelp = require("../../utf/common/apiReturnHelper");
    
    router.use(function (req, res, next) {
      next();
    });
    
     
    router.post("/report", async function (req, res) {
      if (!req.session.currentUser){
        return res.json(apiHelp.noSession());
      }
    
      if (!req.body.data || !req.body.functionName){
        return res.json(apiHelp.error({message:"missingData"}));
      }
    
      let data = req.body.data;
      let functionName = req.body.functionName;
      let unixTimeStamp = new Date().getTime() / 1000; //unix timestamp
      let fileName = unixTimeStamp + "_report.pdf";
      let path = "temp/" + fileName;
    
      await fetchReport(path, data, functionName);
      fs.createReadStream(path).pipe(res).on('end', res.end);
      fs.access(path, fs.constants.R_OK, (err) => {
        console.log("\n> Checking Permission for reading the file");
        if (err) console.error("No Read access");
        else { 
          setTimeout(()=> {  
          fs.unlink(path, function (err) {
            if (err) {
              console.log("No file found");
            }
            console.log("File deleted!");})
          },3000);}
      });
    
    
    });
    
    var fetchReport = async (path, data, functionName) => {
      try {
        let jsr = process.env.JSREPORTURL + "/api/" + functionName;
    
        let options = {
          // auth: { user: 'admin', password: 'samer'},
          url: jsr, //valid string to report api
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
          responseType: "stream",
          maxContentLength: Infinity,
          maxBodyLength: Infinity,
        };
    
        let response = await axios.post(
          jsr,
          {
            template: { name: "invoice" },
            data: data,
          },
          options
        );
        let writeStream = response.data.pipe(fs.createWriteStream(path))
        .on('finish', () => {
          console.log("DONE writing");
          return;
        });;
    
      } catch (e) {
        console.log(e);
      }
    };
    
    module.exports = router;
    

    However, after deploying the jsreport app to Azure functions, here what's happening:

    When I attempt to download my pdf file first time, it downloads fine:

    0_1602005320070_upload-81424283-815e-4711-9791-9f925d335e18

    0_1602005342609_upload-fd4bec94-49dd-42a6-b22d-21230910c2fc

    However....when I call the same request for 2nd, 3rd time...etc, the pdf file is being incomplete and of course corrupt (can't open):
    0_1602005514077_upload-860b3c30-af5c-4f6a-a8e5-13c382eb474e

    Notice how the file size is being smaller in the 2nd and 3rd time.

    This is happening only on Azure Functions and not on local, however the reports being downloaded to the temp folder in backend are being fine.

    I dunno what I am doing wrong, I feel the may be a delay issue from Azure Functions and sending file before finishing the read stream?



  • I solved it by wraping the fetchReport function with new Promise, after doing some logging I realized that the fetchReprot (which has the write stream) is finishing before sending the file to client:

    var fetchReport =  (path, data, functionName) => {
        return new Promise(async resolve =>  { 
        let jsr = process.env.JSREPORTURL + "/api/" + functionName;
    
        let options = {
          // auth: { user: 'admin', password: 'samer'},
          url: jsr, //valid string to report api
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
          responseType: "stream",
          maxContentLength: Infinity,
          maxBodyLength: Infinity,
        };
    
        let response = await axios.post(
          jsr,
          {
            template: { name: "invoice" },
            data: data,
          },
          options
        );
        let writeStream = response.data.pipe(fs.createWriteStream(path))
        .on('finish', () => {
          console.log("ONE");
          resolve(writeStream);
        })
    
      })
     
    };
    

    But I am not sure why it works, I thought that a Async function should behave like a function returning a new Promise?


Log in to reply
 

Looks like your connection to jsreport forum was lost, please wait while we try to reconnect.