0

i am afraid i am either too dumb to use google correctly or having a weird problem nobody not knowing what they're doing ran into.

The goal is to upload a file to a server and then download it again.
However, the server is a custom backend which handles all contents as pure byte array, since it is designed to cope with user input as well as machine-to-machine communication.

I am running a vue.js frontend with node.

I am already able to upload a file as byte array and load that byte array back to the frontend, but now i want to provide the user with the possibility to download that byte array as file.
The user has to enter the file type in the download screen and if they enter the wrong type, the file is corrupt. That would be ok.
Right now, even when i enter the correct file type, the file is corrupted (tested with PDFs and Word files).

Here is my code:
Upload:

<input v-if="type==types.file" id="input-field" ref="file" type="file" @change="handleFileUpload()">

      handleFileUpload(){
        this.value = this.$refs.file.files[0];
      },

      submitFile(){
        var reader = new FileReader();
        var self = this;
        reader.onload = function(event){
          self.value = new Uint8Array(event.target.result);
          self.$notify('succes', 'Dateiupload abgeschlossen');
        };
        reader.onerror = function(event){
          self.$notify('error', 'Fehler beim Dateiupload');
        }
        reader.readAsArrayBuffer(this.value);
      },

Download:

parseFileValueFromByteArray(byteArray){
        const downloadElement = document.createElement('a');
        downloadElement.style.display = 'none';
        downloadElement.href = window.URL.createObjectURL(new File(byteArray, this.name));
        downloadElement.download = this.name;
        document.body.appendChild(downloadElement);
        downloadElement.click();
        window.URL.revokeObjectURL(downloadElement.href);
        return this.value;
}

I get the download-Window to open and download the file, however pdf-Readers (or Word, in that case) show "File corrupted".

What am i doing wrong in creating a valid file? I guess i'm somehow encoding file-metadata (since js File is more than the pure bytes) without decoding the bytes correctly.
So how can i revert FileReader.readAsArrayBuffer()back to a file?

Thanks in advance!

Edit:
Weird thing i noticed: Original pdf file size is 99KB. byteArray length is 100392.
File size from new File(byteArray) gives size of 255KB. Converting byte to text (using new TextEncoder("utf-8).decode(byteArray))) gives me something roughly similar to opening a pdf with notepad++, but opening the downloaded file with notepad++ gives me as stream of numbers).
Maybe this help find the error?

Tim
  • 33
  • 1
  • 8
  • Are you sure that `byteArray` is actually an instance of `ArrayBuffer` ? – IVO GELOV Jun 18 '20 at 16:36
  • Hi @IVOGELOV, byteArray is an instance of Uint8Array, parsed from a base64 string: `this.byteArrayPayload = Uint8Array.from(atob(this.rawPayload), c => c.charCodeAt(0))` – Tim Jun 18 '20 at 17:21
  • I think you should actually use `byteArrayPayload.buffer` - it is the real ArrayBuffer. – IVO GELOV Jun 19 '20 at 06:45
  • Switching to `new File(byteArray.buffer, this.name)` gives me `TypeError: File constructor: Argument 1 can't be converted to a sequence.` – Tim Jun 19 '20 at 08:16
  • Of what type is `byteArrayPayload.buffer` then ? – IVO GELOV Jun 19 '20 at 10:08
  • It gives me an ArrayBuffer and i can't find anything unusual about that. – Tim Jun 19 '20 at 10:22
  • You can try with `Blob()` instead of `File()` – IVO GELOV Jun 19 '20 at 10:30
  • Sadly, this doesn't change anything. `window.URL.createObjectURL(new Blob(byteArray))` still gives me the corrupt 251KB pdf file and `window.URL.createObjectURL(new Blob(byteArray.buffer))` throws the same error. – Tim Jun 19 '20 at 12:15
  • The question for me is, why does the file content chenge from something like "%PDF-1.5%µµµµ1 0 obj< – Tim Jun 19 '20 at 13:01

1 Answers1

1

So, i've researched more and found this. I have no idea why, but instead of new Blob(byteArray) i have to donew Blob([byteArray]). This gives me the possibility to set .pdf file type in Download and receive a valid file.

To have everything in one place, what i am doing is:
Upload:

<input id="input-field" ref="file" type="file" @change="handleFileUpload()">
<button @click="submitFile()">Start upload</button>

handleFileUpload(){
    this.value = this.$refs.file.files[0];
},

submitFile(){
    var reader = new FileReader();
    var self = this;
    reader.onload = function(event){
        self.value = new Uint8Array(event.target.result);
        self.$notify('succes', 'File uplaoded');
    };
    reader.readAsArrayBuffer(this.value);
}

Download:

//this.payload is set with base64 representation of upload and then load() is called
load(){
    this.base64ToByteArray();
    this.parseFileValueFromByteArray(this.byteArrayPayload);
},

base64ToByteArray(){
    this.rawPayload = this.payload;
    this.byteArrayPayload = Uint8Array.from(atob(this.rawPayload), c => c.charCodeAt(0))
},

parseFileValueFromByteArray(byteArray){
    const downloadElement = document.createElement('a');
    downloadElement.style.display = 'none';
    downloadElement.href = window.URL.createObjectURL(new Blob([byteArray]));
    downloadElement.download = this.name;
    document.body.appendChild(downloadElement);
    downloadElement.click();
    window.URL.revokeObjectURL(downloadElement.href);
}

Tim
  • 33
  • 1
  • 8