2

I'm trying to download a pdf file from S3, but everytime I download it to my local machine, I receive the error, "Error: Failed to load PDF". It seems I am still downloading a blob instead of a file, but have tried everything and looked through too many stackoverflow solutions.

Here is my backend code where the download function lives (sorry, it's still messy): backend --> middleware --> amazon.js

const AWS = require('aws-sdk');
const fs = require('fs');
const uuid = require('uuid');
require('dotenv').config()
console.log("proccess", process.env.IAM_USER_KEY)
var dataUriToBuffer = require('data-uri-to-buffer');
const _path = require('path');
const { post } = require('../routes/user.data');
const { app } = require('firebase-admin');
const multer = require('multer');

var FileReader = require('filereader')
    , fileReader = new FileReader
    ;

AWS.config.update({
    accessKeyId: process.env.IAM_USER_KEY,
    secretAccessKey: process.env.IAM_USER_SECRET,
    region: 'us-east-1'
})

const s3 = new AWS.S3()

async function upload(req, res, next){
    let key = req.params.source+"/"+req.params.data.fields.id;

    // function to save the user uploaded file to "uploads" folder

    const uploadFile = (fileName) => {
        
        // Read content from the file
        const fileContent = fs.readFileSync(fileName);        
    
        // Setting up S3 upload parameters
        const params = {
            Bucket: process.env.BUCKET,
            Key: key, // File name you want to save as in S3
            Body: fileContent
        };
    
        // Uploading files to the bucket
        s3.upload(params, function(err, data) {
            if (err) {
                throw err;
            }
            console.log(`File uploaded successfully. ${data.Location}`);
        });
    };
    var filePath = _path.resolve(req.params.data.file.name);
    uploadFile(req.params.file_path)
}

async function download(req, res, next){
    let report_object = req.body.object_id
    report_object = report_object[0]
    let source = req.body.source

    let dir = _path.join(__dirname, '..', 'public')
    let fileName = `${source}.pdf`
    let filePath = _path.join(dir, fileName)

    let writeStream = fs.createWriteStream(filePath)

    var params = {
        Key: source+"/"+report_object,
        Bucket: process.env.BUCKET
    }
    const readStream = s3.getObject(params).createReadStream();
    
    // Error handling in read stream
    readStream.on("error", (e) => {
        console.error(e);
        reject(e);
    });
    
    readStream.pipe(writeStream);
    writeStream.on('finish', () => {
        res.download(dir + "/" + fileName)
    })
}

module.exports = { upload, download }

And here is my download function in the api interface that is found in the frontend and communicates with the backend:

import axios from 'axios';
import fs from 'fs'
import streamToBlob from 'stream-to-blob'


async function upload(data, user){
    data["user"] = user
    await axios({
        method: 'post', 
        url: 'http://localhost:3000/s3/upload', 
        data: data,
        headers: { "content-type": "multipart/form-data"},
    })
}

 function download(id, source){
    let data =  axios.post("http://localhost:3000/s3/download", {object_id: id, source: source}, { headers : {'Accept' : "application/pdf"}}).then( (res) => {
        let blob = new Blob([res.data], {type: 'application/pdf'})
        const link = document.createElement('a')
        link.href = URL.createObjectURL(blob)
        link.download = `${source}`
        link.click()
        setTimeout(function() {
            URL.revokeObjectURL(link.href)
        }, 60000)
        
        return
    })
}

export {upload, download}

I believe that the current download is a blob because I see that in the network tab of the dev inspect tools.

Thank you in advance for your help.

yoyobojo
  • 31
  • 1
  • 4
  • Can you try reducing complexity by downloading any online pdf to confirm that it's still broken? Also, please consider using a linter and autoformatter. Future you will thank you tremendously. – cisco Mar 31 '21 at 01:11
  • @fiz I can download the respective files directly from Amazon S3. I cannot seem to convert the blob into a file. – yoyobojo Mar 31 '21 at 01:52

1 Answers1

1

Not sure what version of the AWS SDK you are using. With @aws-sdk/client-s3, version 3.3.0 I was able to:

import { S3 } from '@aws-sdk/client-s3'
const express = require('express')

const expressInstance = express()

const s3Client = new S3({
  region: '...',
  credentials: {
    accessKeyId: '...',
    secretAccessKey: '...'
  }
})

expressInstance.get('/durl', async (req, res) => {

  const object = await s3Client.getObject({
    Bucket: '...',
    Key: decodeURIComponent(req.query.k)
  }

  object.Body.pipe(res)

})

... with a simple front JS:

const link = document.createElement('a')
link.href = '.../durl?k=my.pdf'
link.download = `my.pdf`
link.click()