js - subir una imagen con copy paste (no jquery)

Pegar una imagen en el documento html después de hacer una captura de pantalla

En este ejemplo uso:

- Bootstrap 5 (sin jquery)
- Librería js propia de b5 (bootstrap.min.js)
- Vanilla Js

Inicialmente aparece el documento en blanco sin ningún botón o input.
Solo se visualiza Paste Anywhere que quiere decir que una vez que tengamos una imagen en memoria bien sea por un print screen (pantallazo) o de haber hecho una selección en algún programa de edición de imagen como paint, photoshop, illustrator, figma etc y ejecutado la función de copiar (copy ctrl+c o cmd+c) podemos pegar en cualquier parte de la página.

JS pegar imagen desde clipboard estado inicial JS pegar imagen desde clipboard despues de pegar

Explico el código

La página tiene un listener que esta escuchando el evento paste de la ventana.
Si hay un archivo de imagen en memoria, en el callback del evento paste habrá un array con al menos un objeto File en e.clipboardData.files

En el momento que se "pega" algo pasa lo siguiente:
- El objeto File en memoria se transforma en una URL en binario (blob: binary large object) que es la que se muestra sobre la imagen (franja azul y negro).
- Esta url se asignara al elemento img-pasted y file-upload este último oculto

Con la URL de la imagen cargada en el input file ya podemos hacer un POST al servidor con fetch.
En mi caso lo haré en formato JSON por lo tanto tengo que convertir la url del input en una cadena de texto concretamente en codificación base64

En la caja de texto se puede incluir algún nombre que también se enviará por POST ya queda tratar estos datos en el backend. En mi caso el backend despues de almacenar el archivo devuelve la url pública del mismo que termina siendo asignada al img-pasted.

El código html

<!DOCTYPE html> <html lang="en"> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta charset="UTF-8"> <title>Paste from Clipboard</title> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous" > </head> <body> <div class="container"> <h4>Paste anywhere</h4> <div class="row"> <div class="col-sm-4"> <input type="text" id="txt-name" class="form-control invisible" placeholder="Image name" /> </div> <div class="col-sm-4"> <button type="button" id="btn-upload" class="btn btn-success invisible">Upload</button> </div> <div class="col-sm-4"> <button type="button" id="btn-reset" class="btn btn-primary invisible">Clear</button> </div> </div> <div class="row"> <div class="col-sm-12"> <input type="file" id="file-upload" class="invisible" /> <p id="p-pasted" class="badge overflow-wrap p-1 font-monospace text-dark bg-info invisible" style="width: 75%"></p> <img id="img-pasted" src="#" class="img-fluid border border-info border-3 invisible" /> </div> </div> </div> <!-- toast --> <div class="toast align-items-center text-white bg-primary border-0 mt-3 me-3 position-absolute top-0 end-0" style="background-color:#A9C948 !important;" role="alert" aria-live="assertive" aria-atomic="true" > <div class="d-flex"> <div class="toast-body">-</div> <button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Close"></button> </div> </div> <!-- esto se puede pasar a un archivo --> <script type="module"> //este contenido está más abajo </script>

Código JS

const POST_URL = "/index.php?f=paste_from_clipboard&nohome=1" const $txtname = document.getElementById("txt-name") const $btnreset = document.getElementById("btn-reset") const $btnupload = document.getElementById("btn-upload") const $file = document.getElementById("file-upload") const $image = document.getElementById("img-pasted") const $p = document.getElementById("p-pasted") const $toast = document.querySelectorAll(".toast")[0] const bootToast = new bootstrap.Toast($toast) const show = elements => elements.forEach( element => element.classList.remove('invisible') ) const hide = elements => elements.forEach( element => element.classList.add('invisible') ) const load_image = url => { hide([$image]) $image.src = url if(url) { show([$image,$p]) } }// load_image() const toast = msg => { const $toastbody = $toast.getElementsByClassName("toast-body")[0] if($toastbody) { $toastbody.innerText = msg bootToast.show() } }// toast() const post = data => fetch(POST_URL, { method: "POST", headers: { "Accept": "application/json", "Content-Type": "application/json" }, body: JSON.stringify({ image: data, name: $txtname.value }) }) .then(response => response.json()) .then(result => { console.log("result:", result) $file.value = "" $image.src = "/" + result.file $p.innerText = $image.src hide([$txtname, $btnreset, $btnupload]) toast(result.message) })//post() window.addEventListener("paste", e => { const files = e.clipboardData.files console.log("window.on-paste", files) if (!files) return $file.files = files const url = URL.createObjectURL(files[0]) $p.innerText = url show([$p, $btnupload, $btnreset, $txtname]) load_image(url) $txtname.focus() });//window.on-paste $btnreset.addEventListener("click", () => { $file.value = "" $txtname.value = "" hide([$btnupload, $btnreset, $p, $image, $txtname]) })//btn-reset.on-click $btnupload.addEventListener("click", () => { const objfile = $file.files[0] if(!objfile) { return toast("No image pasted") } const reader = new FileReader() reader.readAsDataURL(objfile) reader.onloadend = () => post(reader.result) })//btn-upload.on-click

Dejo un enlace al código fuente en mi github

Autor: Eduardo A. F.
Publicado: 19-04-2021 20:48