1

To upload I file in Angular, I am doing this:

<input type="file" (change)="onFileSelected($event.target.files)"/> 
<button (click)="onStartUpload()"</button>

  public onFileSelected(files: File[]) {
    this.file = files[0];
  }

  public onStartUpload() {
    // Upload the file
  }

This works perfectly. But when I select a file, then change its content and save it, and then upload it, my backend response is this:

Unexpected end of Stream, the content may have already been read by another component.

This only happens with Firefox. It works fine in Chrome.

Update: With Chrome's latest update, it does not send the request anymore.

How can I check if the file has been changed after I selected it in the browser?

Tim
  • 3,910
  • 8
  • 45
  • 80
  • Just for the sake of clarity, are you primarily interested in ensuring the upload works, or just in checking if it's been changed? – enhzflep Apr 23 '20 at 10:06
  • checking if it's been changed :) – Tim Apr 23 '20 at 10:37
  • I suspect it's not possible. If you grab a copy of the file when the file-picker changes and cache it, you get back the same info after altering the file and hitting the button. The length and lastModified date are both reported as being identical - the initial details. – enhzflep Apr 23 '20 at 11:03
  • Do you think this thread could be of any use to you? https://stackoverflow.com/questions/42355858/input-file-onchange-event-not-firing – Mitko Delibaltov Apr 23 '20 at 11:03
  • Thanks but I already know that :) I don't think that the browser caches a copy of the file once it has been selected. Chrome would send the latest version of the file event after it had beed changed after its selection - but since the last update, this does not work anymore. Chrome won't even send the request at all. I wonder why I cannot find anything in recent changelogs about this... – Tim Apr 23 '20 at 11:12
  • 3
    Are you using a FileReader to aquire the contents? If so, that's probably your way out of this hole, since the `onerror` method fires instead of the `onload` one. Here's what the error member of a FileReader contains when the onerror method was called as a result of changing the file after it was chosen: "DOMException: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired." (Chrome 81.0.4044.113 (Official Build) (64-bit)) – enhzflep Apr 23 '20 at 23:59
  • @MitkoDelibaltov - that's the event that the input element fires whenever a file is chosen. It is only fired in response to user action taken in the browser, it is not however fired when the file already selected is subsequently modified. – enhzflep Apr 24 '20 at 00:51
  • @enhzflep that worked. Thank you. Write an answer and I'll accept it. Cheers – Tim Apr 24 '20 at 08:27

1 Answers1

3

If you use an instance of the FileReader object, you can take advantage of the fact that it wont load a file that's been altered after having been selected. In order to upload the file successfully, it must remain the same as it was when it was chosen.

Here's a short example that will demonstrate. Tested on the current iteration of Chrome x64 under windows.

window.addEventListener('load', onLoaded, false);

function onLoaded(evt) {
  document.querySelector('button').addEventListener('click', onBtnClicked, false);
}

function onBtnClicked(evt) {
  let fileToUpload = document.querySelector('input').files[0];

  const fileReader = new FileReader();
  fileReader.onload = fileLoaded;
  fileReader.onerror = fileError;
  fileReader.readAsText(fileToUpload);
  // fileReader.readAsArrayBuffer(fileToUpload);
  // fileReader.readAsBinaryString(fileToUpload);
  // fileReader.readAsDataURL(fileToUpload);

  function fileLoaded(evt) {
    let fileToUpload = evt.target.result;
  }

  function fileError(evt) {
    console.log('Error loading file');
    console.log(evt.target.error);
  }
}
<input type='file' /><br>
<button>Go</button>
enhzflep
  • 12,927
  • 2
  • 32
  • 51