4

I want to be able to paste an image taken via the print screen button in a textarea and upload it to my server.

I am using onpaste and it seems to work, I can get a hold of a file object but whenever I try to send it it's empty.

onpaste(event) {
  if (event.clipboardData.files.length) {
    let file = event.clipboardData.files[0];

    var oReq = new XMLHttpRequest();
    var data = new FormData();

    data.append("file", file);
    data.append("csrf", CSRF_TOKEN);

    oReq.open("POST", exports.url("/file"));
    oReq.setRequestHeader("Accept", "application/json");

    oReq.send(data);
  }
}

I observe the network tab in my dev tools and a request is properly being sent with all of the information about the file except there is no contents

Request payload:

------WebKitFormBoundaryWggS2BbKcZV6v4tn
Content-Disposition: form-data; name="file"; filename="image.png"
Content-Type: image/png


------WebKitFormBoundaryWggS2BbKcZV6v4tn
Content-Disposition: form-data; name="csrf"

58718518696317230756900774635415
------WebKitFormBoundaryWggS2BbKcZV6v4tn--
php_nub_qq
  • 15,199
  • 21
  • 74
  • 144
  • Wouldn't a canvas be more natural target when pasting an image ..? – Teemu Jan 22 '18 at 12:54
  • @Teemu I don't really want to display the image, I just want to send it to a server. – php_nub_qq Jan 22 '18 at 12:55
  • Mhh... then don't display the canvas. you can still build an image to an invisible canvas as well. – Teemu Jan 22 '18 at 12:56
  • @Teemu Why would you put it into an invisible canvas if you don't have to? The data is on his clipboard. He wants to send it to the server. Seems weird to stuff it into a canvas first. – mpen Jan 22 '18 at 19:38

1 Answers1

2

In this case the file you access via event.clipboardData.files[0]; is really just a handle to the file, it doesn't contain the actual file data. To access this you must use a FileReader per the FileAPI documentation.

There are four different ways to read this data through the FileReader:

void readAsArrayBuffer(Blob blob);
void readAsBinaryString(Blob blob);
void readAsText(Blob blob, optional DOMString label);
void readAsDataURL(Blob blob);

See below an example which you can modify to fit your needs.

function onPaste(event) {
  if (event.clipboardData.files.length) {
    let file = event.clipboardData.files[0];
    var oReq = new XMLHttpRequest();
    var data = new FormData();

    data.append("csrf", "TOKEN");

    oReq.open("POST", "/file");
    oReq.setRequestHeader("Accept", "application/json");

    /* Create a new FileReader. */
    var fileReader = new FileReader();
    
    fileReader.onload = function(event) {
      /* Once the file has finished loading, run the following: */
      data.append("file", this.result);
      oReq.send(data);
    };
    
    /* Tell the file reader to asynchronously load the files contents. */
    fileReader.readAsDataURL(file);
  }
}
<textarea onpaste="onPaste(event)" ></textarea>
Gaurang Tandon
  • 6,504
  • 11
  • 47
  • 84
  • Well this is a little confusing. How come I can send files from drop events or file inputs directly by passing in the File instance, but I can't in this case? Reading the file will not really cut it in my case, since on the other end the file will arrive as a string and not as a file (PHP) and I can't modify it. Thanks for the suggestion! – php_nub_qq Jan 22 '18 at 20:40
  • From my understanding the same applies. When you paste a file into a file input you don't actually 'upload' the file, this only happens when the form is submitted, and is handled by the browser. See [Using files from web applications](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications) - specifically see the example titled 'Example: Showing thumbnails of user-selected images'. The code displayed there is very similar to that which is posted above. –  Jan 22 '18 at 21:01
  • Presumably your PHP code would need to convert the encoded string into a file object [like so](https://stackoverflow.com/questions/15153776/convert-base64-string-to-an-image-file) –  Jan 22 '18 at 21:02
  • Yes I understand what you mean but the backend code is already written and I can not modify it. I don't understand however why I can append a file from an input or a drop event to a formdata object, but I can't append one from a paste event. – php_nub_qq Jan 22 '18 at 21:18
  • Obviously the browser will read the file in order to send it, but ever since HTML5 you just give FormData an instance of File and it all works behind the scenes, which is not the case now? – php_nub_qq Jan 22 '18 at 21:19
  • To my knowledge (and all of the resources I've linked) this is simply the way file uploads are handled through Javascript. What format does your backend expect? I believe a base64 encoded string (like in my example) is standard, but it could be converted to another format with ease. –  Jan 22 '18 at 21:42
  • If I were to use the exact same function but I bind it to `onchange` on a input with type file and I get the file from `event.target.files[0]` it would work as it is, that is what I'm trying to explain and it's confusing for me why it would not work now. – php_nub_qq Jan 22 '18 at 22:42