Search code examples
ruby-on-railsreactjsapirails-activestoragewicked-pdf

Rails API / react / axios downloads corrupted file


First I created a pdf with WickedPDF.

pdf_string = WickedPdf.new.pdf_from_string(
  ActionController::Base.new.render_to_string(template: 'v1/invoices/invoice_template', filename: 'test.pdf')
)

invoice.attachment.attach(
  io: StringIO.new(pdf_string),
  filename: 'test.pdf',
  content_type: 'application/pdf'
)

My app is setup to store the files on s3 on prod and locally in dev. For testing I also used s3 in dev to verify that my pdf is getting generated and saved correctly. So after it has been generated I am able to log into aws and download my invoice. Everything displays just fine.

Now the problem I have is trying to download my invoice. When I download it, my pdf is just blank.

I have a download method that looks like this:

    response.headers['Content-Type'] = @invoice.attachment.content_type
    response.headers['Content-Disposition'] = "inline; #{@invoice.attachment.filename}"

    response.headers['filename'] = @invoice.filename

    @invoice.attachment.download do |chunk|
      response.stream.write(chunk)
    end

I also tried

    send_data @invoice.attachment.download, filename: @invoice.filename

and my frontend (react) uses axios to download it:

  const downloadInvoice = (id) => {
    axios.get(`/v1/invoices/${id}/download`)
      .then((response) => {
          const url = window.URL.createObjectURL(new Blob([response.data]));
          const link = document.createElement('a');
          link.href = url;
          link.setAttribute('download', response.headers.filename);
          document.body.appendChild(link);
          link.click();
      })
      .catch(() => {});
  };

I am a little confused to why my downloaded pdf is blank. If I open it in my storage folders it displays just fine. There seems to be an issue with how I download it.

What works is if I create a presigned URL for S3 with:

s3 = Aws::S3::Resource.new(client: aws_client)
bucket = s3.bucket('bucket-name')
obj = bucket.object("@invoice.attachment.attachment.blob.key)

url = obj.presigned_url(:get)

I can send that url back to the frontend and open it in a new tab to view the pdf. But this is not what I want...

Thanks for any help!


Solution

  • In case anyone is interested in this or runs into the same issue. I hope this will save you some time!

    The problem is with the axios request.

    Instead of:

      axios.get(`/v1/invoices/${id}/download`)
    

    use

      axios.get(`/v1/invoices/${id}/download`, { responseType: 'arraybuffer' })