Tengo un formulario HTML simple que envía un archivo seleccionado a través de AJAX a un punto final de API.
Si hago los siguientes pasos:
Chrome fallará la segunda solicitud con un error: net::ERR_UPLOAD_FILE_CHANGED . Tenga en cuenta que si cambia el archivo antes de la carga inicial, el archivo se cargará sin problemas. El error ocurre solo en la segunda carga, cuando cambia el archivo después de una carga inicial exitosa. Estoy probando esto con archivos CSV y cambiándolos en un editor de texto.
No parece haber una manera de aislar ese error.
¿Hay alguna forma de evitar esto?
De lo contrario, ¿es posible detectar este error específico y mostrar un mensaje significativo al usuario? El error que devuelve Fetch en la promesa no tiene información específica al respecto. El único lugar donde veo ERR_UPLOAD_FILE_CHANGED es en la consola de desarrollo del navegador.
Estoy bastante seguro de que no hubo ningún problema con esto hace aproximadamente un año (principios de 2019), ya que la posibilidad de volver a cargar un archivo modificado funcionó muy bien en nuestro flujo de interfaz de usuario. Ahora tenemos que obligar al usuario a elegir el archivo de nuevo. Así que supuse que esto se introdujo con una actualización reciente de Chrome.
Aquí hay un fragmento simplificado del código:
<html> <head> <script type='text/javascript'> document.addEventListener("DOMContentLoaded", function(){ const button = document.querySelector('#btnSubmit'); button.addEventListener('click', () => { const form = new FormData(document.querySelector('#theForm')); const url = '/my-api' const request = new Request(url, { method: 'POST', body: form }); fetch(request).then(function() { console.log("ok"); }).catch(function(err) { console.log(err); }); }); }); </script> </head> <body> <form enctype="multipart/form-data" action="" method="post" id='theForm'> <input type="file" name="csvfile" id="csvfile" value="" /></td> <input type="button" name="uploadCSV" value="Upload" id='btnSubmit'/> </form> </body> </html>EDITAR : el error está marcado como WontFix en bugs.chromium.org: https://bugs.chromium.org/p/chromium/issues/detail?id=1086707#c7
Simplemente verificaría file.lastModified entre los clics del botón de carga y, si es diferente, restablecería los valores del formulario.
https://developer.mozilla.org/en-US/docs/Web/API/File/lastModified
Una mejor solución sería leer el archivo en un ArrayBuffer y cargarlo.
Logré obtener un error más descriptivo usando el Lector de archivos javascript. Después de cambiar el archivo y presionar el botón por segunda vez, FileReader muestra un mensaje algo más descriptivo:
DOMException: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.
Al menos, este es un error que se puede detectar y mostrar al usuario, para que sepa que debe volver a seleccionar el archivo.
Aquí está el fragmento:
<html> <head> <title>Test</title> <script type='text/javascript'> document.addEventListener("DOMContentLoaded", function(){ const button = document.querySelector('#btnSubmit'); button.addEventListener('click', () => { let inputElement = document.getElementById('csvfile'); for (let i = 0; i < inputElement.files.length; i++) { const file = inputElement.files[i]; console.log(file); const reader = new FileReader(); reader.readAsText(file, "UTF-8"); reader.onload = function (evt) { console.log(evt.target.result); } reader.onerror = function (err) { // read error caught here console.log(err); console.log(err.target.error); } } }); }); </script> </head> <body> <form enctype="multipart/form-data" action="" method="post" id='theForm'> <input type="file" name="csvfile" id="csvfile" value="" /> <input type="button" name="uploadCSV" value="Upload" id='btnSubmit'/> </form> </body> </html>Parece que no hay forma de evitar esto en este momento (junio de 2020). Hay un error abierto en Chromium:
https://bugs.chromium.org/p/chromium/issues/detail?id=1084880&q=ERR_UPLOAD_FILE_CHANGED&can=2
No, si el usuario ha cambiado el archivo, ya es otro archivo, por lo que no puede (no debe) obtener acceso a este archivo sin la acción explícita del usuario; de lo contrario, sería un problema de seguridad.
Puede guardar el archivo en la memoria cuando el usuario lo carga.
document.getElementById('fileInput').addEventListener('change', function() { saveFileConentInMemory(this.files[0].arrayBuffer()); });Y cuando el usuario presione el botón "Enviar", simplemente obtenga este contenido de la memoria y envíelo
button.addEventListener('click', () => { const file = getFileContentFromMemeory(); send(file); })Sí, no puede estar seguro de enviar la última versión del archivo, pero debe asegurarse de enviar el contenido que se cargó.
Además, debe tener en cuenta el consumo de memoria y la API asíncrona de lectura del archivo (por lo que aún puede obtener este error sobre el contenido modificado incluso cuando lo escribe en la memoria)
Una solución rápida sería incluir un parámetro complete para la llamada AJAX (o cualquier equivalente de una llamada finally que siempre se invoca) y agregar un código que restablece el formulario. He notado que adjuntar el archivo nuevamente lo resuelve. Si bien aprecio que no sea una solución real al problema, proporciona manejo o, más bien, prevención de ese error.
De esta manera, si el usuario por alguna razón necesita volver a cargar el archivo, tendrá que elegir el archivo nuevamente.
Ejemplo a continuación:
$.ajax({ url: this.action, type: this.method, data: this.data, success: function (response) { // success scenario }, error: function (result) { // error scenario }, complete: function (data) { $('#uploadForm')[0].reset(); // this will reset the form fields } });Simplemente reinicie el archivo de tipo de entrada después de los mensajes de aviso o el procesamiento de los datos
$('#file').val("");$("#id_of_your_file_input").click(function(){ $("#id_of_your_file_input").val(''); });Simplemente agregue el script jQuery anterior para manejar esta excepción del navegador. Lo que hace es borrar la entrada del archivo cada vez que alguien hace clic en él, por lo que eliminará la selección actual.