Search code examples
javascriptpdffilereader

FileReader losing data when reading PDF


I have the constraint of sending data to a server in JSON format only, and I need to send a PDF file together with other form data in the JSON. I though I could make a string from it with base64 like in this solution I found on SO:

let data = {foo: 1, bar: 2};
let reader = new FileReader();
reader.readAsDataURL(pdf);
reader.onload = () => {
  data.file = reader.result;
  $.ajax({type: 'POST', dataType: "json", data: JSON.stringify(data), ...});
}

But it happened that reader.result does not contain the whole PDF (whether I save it to file instead of sending, or get it back from the server). In a text editor the content is the same, but a binary editor says that some bytes are missing. My browser can load it as a pdf and the title shows, but the page is blank.

I also tried reader.readAsBinaryString and transform to base64 myself with btoa, but same result.

Edit: CodePen example: https://codepen.io/jdelafon/pen/eRWLdz?editors=1011

Edit: to verify, I did this:

let reader = new FileReader();
reader.readAsBinaryString(pdf);
reader.onload = () => {
    let blob = reader.result;
    let file = new Blob([blob], {type: 'application/pdf'});
    let fileURL = window.URL.createObjectURL(file);
    // make it display in <embed>
};

The body of the pdf is empty. If I replace file by the original pdf, it gets displayed entirely. So FileReader loses data in the process.

It knows the title and number of pages, but the body of each page is empty

Is there a better way? Is it an encoding issue with FileReader?

I also though about using FormData like this:

let data = {foo: 1, bar: 2};
let fd = new FormData();
fd.append('file', pdf);
data.file = btoa(fd);
$.ajax({type: 'POST', dataType: "json", data: JSON.stringify(data), ...});

But now when I fetch the data back from the server, JS has no idea that it represents a FormData object, so I don't know how to get the file back for display in a browser. Is it possible?


Solution

  • Watilin's answer was not added, so here is the working snippet copied from Codepen:

    function onUpload(input) {  
      let originalFile = input.files[0];
      let reader = new FileReader();
      reader.readAsDataURL(originalFile);
      reader.onload = () => {
        let json = JSON.stringify({ dataURL: reader.result });
    
        // View the file
        let fileURL = JSON.parse(json).dataURL;
        $("#display-pdf").empty();
        $("#display-pdf").append(`<object data="${fileURL}"
          type="application/pdf" width="400px" height="200px">
        </object>`);
    
        // View the original file
        let originalFileURL = URL.createObjectURL(originalFile);
        $("#display-pdf").append(`<object data="${originalFileURL}"
          type="application/pdf" width="400px" height="200px">
        </object>`)
        .onload(() => {
          URL.revokeObjectURL(originalFileURL);
        });
      };
    }
    
    

    HTML:

    <input id="file-input" type="file" onchange="onUpload(this)" />
    <div id="display-pdf"></div>