type read example javascript html html5 file-upload fileapi

read - upload file javascript ajax



Recordar y repoblar la entrada de archivo (4)

A riesgo de dar un paso al frente de la tremenda información proporcionada por GitaarLAB, podría sugerir que DaveWalley está muy cerca brindando una solución práctica al problema. Ustedes dos me ayudaron mucho, así que gracias.

Mi mensaje para llevar es,

  1. La entrada de archivo ofrece una calle de sentido único. Está ahí esperando para manejar tu próxima carga. Eso es todo lo que hace, más o menos. Recibe
  2. Una entrada de formulario de texto div o de solo lectura cerca de la etiqueta para la entrada de su archivo puede servir . es decir, se puede utilizar para decirle al usuario: "Aquí está lo que está actualmente cargado:" - el nombre del archivo que se mostrará tendrá que completarse desde la lógica del lado del servidor.

Entonces, un caso de uso básico sería:

  • el usuario carga el archivo a través de la entrada de formulario en la página web
  • La lógica del lado del servidor almacena el archivo
  • En un ciclo de Ajax o una página web, vuelva a cargar el código del lado del servidor para identificar una cadena fileName para escribir en el div, "Aquí está lo que se carga actualmente".
  • La entrada de archivo también se puede volver a presentar, de modo que el usuario también puede subir otro archivo / en su lugar.

Es posible que deba hacer un poco más de trabajo para manejar una validación "* requerida", pero esa es otra historia.

Nota:

La (s) respuesta (s) a continuación reflejan el estado de los navegadores heredados en 2009. Ahora puede establecer el valor del elemento de entrada de archivo a través de JavaScript en 2017.

Consulte la respuesta en esta pregunta para obtener detalles y una demostración:
Cómo establecer el valor de entrada de archivo de manera programática (es decir, cuando se arrastran archivos)

Tengo un sitio web que permite al usuario cargar un archivo varias veces para su procesamiento. Por el momento tengo una sola entrada de archivo pero quiero poder recordar la elección de los usuarios y mostrarla en la pantalla.

Lo que quiero saber cómo hacer es que, después de que un usuario seleccione un archivo, recuerde su elección y vuelva a mostrar la entrada del archivo con el archivo preseleccionado al volver a cargar la página. Todo lo que necesito saber es cómo recordar y volver a llenar una entrada de archivo.

También estoy abierto a enfoques que no usan una entrada de archivo (si eso es posible).

Estoy usando JQuery


Crea un campo de entrada en tu formulario. Cuando el usuario selecciona un archivo, copie el resultado en este campo, algo como:

jQuery(''#inFile'').change( function(){ jQuery(''#inCopy'').val( jQuery(''#inFile'').val() ); } );

En realidad, el resultado no se copia exactamente, sino que copia "C: / fakepath / SELECTED_FILE_NAME". Si bien no está permitido establecer el valor de una entrada de archivo, puede establecer el valor del campo de entrada de texto, sin el "C: / ruta_de_fake /", mientras el servidor prepara el formulario.

Ahora, cuando el servidor recupera el formulario, verifique el campo de entrada de texto. Si comienza con "C: / fakepath /", el usuario debe haber seleccionado un nuevo archivo, así que cargue su nueva selección. Si no es así, el usuario ha optado por la selección previa, lo que no debería ser un problema ya que, de acuerdo con la pregunta original, la opción anterior se ha cargado antes y DEBERÍA (al menos con la programación adecuada, PODRÍA) en el servidor.


Ok, desea "Recordar y repoblar la entrada de archivos ", "recordar su elección y volver a mostrar la entrada del archivo con el archivo preseleccionado en la recarga de la página ".
Y en el comentario a mi respuesta anterior usted afirma que no está realmente abierto a las alternativas: "Lo siento, pero no hay Flash ni Applets, solo javscript y / o entrada de archivos, posiblemente arrastrando y soltando".

Noté que mientras navegaba (bastantes) preguntas duplicadas ( 1 , 2 , 3 , etc.) , prácticamente todas las demás respuestas son del tipo: "No, no puedes, eso sería un problema de seguridad", opcionalmente seguido mediante un simple ejemplo conceptual o de código que describe el riesgo de seguridad.

Sin embargo, alguien terco como una mula (no necesariamente algo malo hasta un cierto nivel) podría percibir esas respuestas como: "No, porque yo lo dije", que de hecho es algo diferente a continuación: "No, y aquí están las especificaciones que dis-allow it ".
Así que este, es mi tercer y último intento de responder a tu pregunta (te guié hasta el abrevadero, te llevo al río, ahora te empujo a la fuente, pero no puedo hacerte beber).

Editar 3:

Lo que quiere hacer en realidad fue una vez descrito / ''sugerido'' en RFC1867 Sección 3.4:

El atributo VALOR se puede usar con las etiquetas <INPUT TYPE=file> para un nombre de archivo predeterminado. Este uso es probablemente dependiente de la plataforma. Sin embargo, podría ser útil en secuencias de más de una transacción, por ejemplo, para evitar que el usuario solicite el mismo nombre de archivo una y otra vez.

Y, de hecho, la sección de especificaciones HTML 4.01 17.4.1 especifica que:

Los agentes de usuario pueden usar el valor del atributo de valor como nombre de archivo inicial.

(Por ''Agentes de usuario'' significa ''navegadores'').

Dado que javascript puede modificar y enviar un formulario (incluida una entrada de archivo) y uno puede usar css para ocultar formularios / elementos de formulario (como la entrada de archivo), las declaraciones anteriores por sí solos permitirían subir silenciosamente archivos de la computadora de un usuario sin su intención / conocimiento.
Es claramente muy importante que esto no sea posible, y como tal, (arriba) RFC1867 establece en la sección 8 consideraciones de seguridad :

Es importante que un agente de usuario no envíe ningún archivo que el usuario no haya solicitado explícitamente que se envíe. Por lo tanto, se espera que los agentes de interpretación HTML confirmen cualquier nombre de archivo predeterminado que pueda sugerirse con <INPUT TYPE=file VALUE="yyyy"> .

Sin embargo, el único navegador (que conozco) que alguna vez implementó estas características fue (algunas versiones anteriores de) Opera : aceptó un <input type="file" value="C:/foo/bar.txt> o valor establecido por javascript ( elm_input_file.value=''c://foo//bar.txt''; ).
Cuando este cuadro de archivo no se modificó al enviar el formulario, Opera desplegaba una ventana de seguridad que informaba al usuario de qué archivo (s) estaba por subir a qué ubicación (url / webserver).

Ahora uno podría argumentar que todos los otros navegadores estaban en violación de la especificación, pero eso sería incorrecto: ya que la especificación declaró: " may " ( no dijo " must ") ".. use el atributo de valor como el nombre inicial del archivo" .
Y, si el navegador no acepta establecer el valor de entrada de archivo (es decir, tener ese valor solo como ''de solo lectura''), el navegador tampoco necesitaría mostrar una seguridad ''atemorizante'' y ''difícil'' -pop-up (que incluso podría no cumplir su propósito si el usuario no lo entendía (y / o estaba ''condicionado'' a hacer siempre clic en ''OK'')).

Avancemos rápidamente a HTML 5 luego ...
Aquí se aclara toda esta ambigüedad (sin embargo, todavía requiere algo de desconcierto):

En el estado 4.10.7.1.18 Carga de archivos , podemos leer los detalles de la contabilidad :

  • El valor del atributo IDL está en modo nombre de archivo.
    ...
  • El atributo de valor del elemento debe ser omitido.

Por lo tanto, se debe omitir el atributo de valor de una entrada de archivo, pero también funciona en algún tipo de ''modo'' llamado ''nombre de archivo'' que se describe en 4.10.7.4 API de elementos de entrada común :

El atributo value IDL permite que los scripts manipulen el valor de un elemento de entrada. El atributo está en uno de los siguientes modos, que definen su comportamiento:

saltando a este '' nombre de archivo de modo '':

Al recibir, debe devolver la cadena "C: / fakepath /" seguida del nombre del archivo del primer archivo en la lista de archivos seleccionados, si existe, o la cadena vacía si la lista está vacía. En la configuración, si el nuevo valor es la cadena vacía, debe vaciar la lista de archivos seleccionados; de lo contrario, debe lanzar una excepción InvalidStateError.

Permítame repetirlo: " must lanzar una excepción InvalidStateError " si se intenta establecer un valor de entrada de archivo a una cadena que no está vacía. (Pero uno puede borrar el campo de entrada configurando su valor en una cadena vacía).

Por lo tanto, actualmente y en el futuro previsible de HTML5 (y en el pasado, excepto Opera), solo el usuario puede completar una entrada de archivo (a través del navegador o el ''selector de archivos'' proporcionado por el sistema operativo). Uno no puede (re) poblar la entrada de archivo a un archivo / directorio con javascript o establecer el valor predeterminado.

Obtener el nombre de archivo / ruta de archivo

Ahora, supongamos que no fue imposible (re) poblar una entrada de archivo con un valor predeterminado, entonces obviamente necesitaría la ruta completa: directorio + nombre de archivo (+ extensión).

En el pasado, algunos navegadores como (más notable) IE6 (hasta IE8) revelaron la ruta completa + nombre de archivo como valor: solo una alert( elm_input_file.value ); simple alert( elm_input_file.value ); etc. en javascript Y el navegador también envió esta ruta completa + nombre de archivo (+ extensión) al servidor receptor en form-submit.
Nota: algunos navegadores también tienen un atributo ''file o fileName'' (generalmente enviado al servidor) pero obviamente esto no incluiría una ruta ...

Ese es un riesgo realista de seguridad / privacidad: un sitio web malicioso (propietario / explotador) podría obtener la ruta al directorio de inicio de un usuario (donde cosas personales, cuentas, cookies, porción de registro del usuario, historial, favoritos, escritorio, etc. ubicado en ubicaciones constantes conocidas) cuando el usuario de Windows no tecnológico típico cargará sus archivos desde: C:/Documents and Settings/[UserName]/My Documents/My Pictures/kinky_stuff/image.ext .
¡Ni siquiera hablé de los riesgos mientras transmitía los datos (incluso "encriptados" a través de https) o el almacenamiento "seguro" de estos datos!

Como tal, cada vez más navegadores alternativos estaban empezando a seguir una de las medidas de seguridad probadas más antiguas: compartir información según la necesidad de conocerla.
Y la gran mayoría de los sitios web no necesitan conocer la ruta del archivo, por lo que solo revelaron el nombre del archivo (+ extensión).

Para cuando se lanzó IE8, MS decidió seguir a la competencia y agregó una opción de URLAction, llamada "Incluir ruta de directorio local al cargar archivos", que estaba configurada como "deshabilitada" para la zona general de Internet (y "habilitada" en el zona de confianza) de forma predeterminada.

Este cambio creó un pequeño estrago (principalmente en entornos ''optimizados para IE'') donde todo tipo de código personalizado y ''controles'' propietarios no podían obtener el nombre de archivo de los archivos cargados: estaban codificados para esperar una cadena que contenía una ruta completa y extraer la parte después de la última barra invertida (o barra inclinada si tuvo suerte ...). 1 , 2

A lo largo vino HTML5,
y como ha leído arriba, el ''nombre de archivo de modo'' especifica:

Al recibir, debe devolver la cadena "C: / fakepath /" seguida del nombre del archivo del primer archivo en la lista de archivos seleccionados, si existe, o la cadena vacía si la lista está vacía.

y ellos notan que

Este requisito del "camino falso" es un triste accidente de la historia

y

Por razones históricas, el valor del atributo IDL prefija el nombre de archivo con la cadena "C: / fakepath /". Algunos agentes de usuario heredados en realidad incluían la ruta completa (que era una vulnerabilidad de seguridad). Como resultado de esto, obtener el nombre de archivo del atributo IDL de valor de una manera compatible hacia atrás no es trivial. La siguiente función extrae el nombre de archivo de una manera adecuada compatible:

function extractFilename(path) { if (path.substr(0, 12) == "C://fakepath//") return path.substr(12); // modern browser var x; x = path.lastIndexOf(''/''); if (x >= 0) // Unix-based path return path.substr(x+1); x = path.lastIndexOf(''//'); if (x >= 0) // Windows-based path return path.substr(x+1); return path; // just the filename }

Nota: Creo que esta función es estúpida: el punto es tener siempre una ruta de Windows falsa para analizar ... Por lo tanto, el primer ''si'' no solo es inútil, sino que incluso invita a un error: imagina un usuario con un navegador anterior que carga un archivo de: c:/fakepath/Some folder/file.ext (como lo devolvería: Some folder/file.ext ) ...
Simplemente usaría:

function extractFilename(s){ // returns string containing everything from the end of the string // that is not a back/forward slash or an empty string on error // so one can check if return_value==='''' return (typeof s===''string'' && (s=s.match(/[^////]+$/)) && s[0]) || ''''; }

(como la especificación de HTML5 claramente intencionada).

Recapitulemos (obtener la ruta / nombre del archivo):

  • navegadores más antiguos (y navegadores más nuevos donde uno podría habilitar esto como una opción como IE> = 8) revelarán una ruta completa de Windows / Unix
  • navegadores menos antiguos no revelarán ninguna ruta, solo un nombre de archivo (+ extensión)
  • Los navegadores actuales / futuros / compatibles con HTML5 siempre pre-colgarán la cadena: c:/fakepath/ al nombre del archivo al obtener el valor de la entrada del archivo
    Además de eso, solo devolverán el primer nombre de archivo (de una "lista de archivos seleccionados") si la entrada de archivo acepta múltiples archivos y el usuario ha seleccionado varios archivos.

Por lo tanto, en el pasado reciente, actualmente y en el futuro HTML5 previsible, por lo general solo obtendrá el nombre de archivo.

Eso nos lleva a lo último que tenemos que examinar: esta ''lista de archivos seleccionados'' / archivos múltiples, que nos lleva a la tercera parte del rompecabezas:

(HTML5) File API

En primer lugar, la "API de archivo" no se debe confundir con la " API del sistema de archivos ", aquí está el resumen de la API del sistema de archivos:

Esta especificación define una API para navegar por las jerarquías del sistema de archivos y define un medio por el cual un agente de usuario puede exponer secciones de espacio aislado del sistema de archivos local de un usuario a las aplicaciones web. Se basa en [FILE-WRITER-ED], que a su vez se basa en [FILE-API-ED], y cada uno agrega un tipo diferente de funcionalidad.

Las ''secciones de espacio aislado del sistema de archivos local de un usuario'' ya indican claramente que no se puede usar para obtener archivos de usuario fuera del entorno limitado (por lo que no es relevante para la pregunta, aunque se podría copiar el archivo seleccionado por el usuario el almacenamiento local persistente y volver a cargar esa copia usando AJAX, etc. Útil como ''reintento'' en la carga fallida. Pero no sería un puntero al archivo original que podría haber cambiado mientras tanto).
Aún más importante es el hecho de que solo webkit (piense en versiones anteriores de Chrome) implementó esta característica y la especificación probablemente no va a sobrevivir ya que is no more actively maintained, the specification is abandonned for the moment as it didn''t get any significant traction

Continuemos con la '' API de archivos '',
es abstracto nos dice:

Esta especificación proporciona una API para representar objetos de archivos en aplicaciones web, así como para seleccionarlos y acceder a sus datos mediante programación. Esto incluye:

  • Una interfaz FileList, que representa una matriz de archivos seleccionados individualmente del sistema subyacente. La interfaz de usuario para la selección se puede invocar a través de <input type="file"> , es decir, cuando el elemento de entrada se encuentra en el estado Carga de archivos [HTML].
  • Una interfaz Blob, que representa datos binarios en bruto inmutables, y permite el acceso a rangos de bytes dentro del objeto Blob como un Blob separado.
  • Una interfaz de archivo, que incluye atributos informativos de solo lectura sobre un archivo, como su nombre y la fecha de la última modificación (en el disco) del archivo.
  • Una interfaz FileReader, que proporciona métodos para leer un archivo o una Blob, y un modelo de eventos para obtener los resultados de estas lecturas.
  • Un esquema de URL para usar con datos binarios, como archivos, para que puedan ser referenciados dentro de las aplicaciones web.

Por lo tanto, FileList se puede completar con un campo de entrada en modo de archivo: <input type="file"> .
¡Eso significa que todo lo anterior sobre el atributo de valor todavía se aplica!

Cuando un campo de entrada está en modo archivo, obtiene un files atributos de solo lectura que es un FileList object similar a una matriz que hace referencia a los archivos seleccionados por el usuario del elemento de entrada y es (/ are) accesible mediante la FileList interface .
¿ Mencioné que los files -attributo del tipo FileList son de solo lectura (API API sección 5.2) ? :

La interfaz HTMLInputElement [HTML] tiene un atributo readonly de tipo FileList ...

Bueno, ¿qué hay de arrastrar y soltar ?

De la documentación de mdn - Seleccionar archivos usando arrastrar y soltar

La magia real ocurre en la función drop ():

function drop(e) { e.stopPropagation(); e.preventDefault(); var dt = e.dataTransfer; var files = dt.files; handleFiles(files); }

Aquí, recuperamos el campo dataTransfer del evento, luego extraemos la lista de archivos, pasándolo a handleFiles (). A partir de este punto, manejar los archivos es el mismo ya sea que el usuario use el elemento de entrada o arrastre y suelte.

Entonces, (al igual que el tipo de campo de entrada = "archivo"), el atributo dataTransfer del evento tiene un files atributos similar a una matriz que es un FileList object similar a una matriz y acabamos de aprender (arriba) que FileList es de solo lectura ..

FileList contiene referencias a los archivos que un usuario seleccionó (o que dejó caer en un drop-target) y algunos atributos. Desde File API Section 7.2 File Attributes podemos leer:

nombre

El nombre del archivo; al obtener, esto debe devolver el nombre del archivo como una cadena. Existen numerosas variaciones de nombre de archivo en diferentes sistemas; este es simplemente el nombre del archivo, sin información de ruta. Al obtener, si los agentes de usuario no pueden hacer que esta información esté disponible, deben devolver la cadena vacía.

lastModifiedDate

La última fecha de modificación del archivo. Al recibir, si los agentes de usuario pueden hacer que esta información esté disponible, debe devolver un nuevo objeto de Fecha [HTML] inicializado a la última fecha de modificación del archivo. Si no se conoce la fecha y la hora de la última modificación, el atributo debe devolver la fecha y la hora actuales como un objeto Date.

y hay un atributo de size :

F.size es igual que el tamaño del argumento fileBits Blob, que debe ser el dato bruto inmutable de F.

Nuevamente, no hay una ruta, solo el nombre de archivo de solo lectura .

Así:

  • (elm_input||event.dataTransfer).files da el objeto FileList.
  • (elm_input||event.dataTransfer).files.length da la cantidad de archivos.
  • (elm_input||event.dataTransfer).files[0] es el primer archivo seleccionado.
  • (elm_input||event.dataTransfer).files[0].name es el nombre de archivo del primer archivo seleccionado
    (y este es el value que se devuelve desde un tipo de entrada = "archivo").

¿Qué pasa con este ''esquema de URL para usar con datos binarios, como archivos, para que puedan ser referenciados dentro de las aplicaciones web'', seguramente que puede contener una referencia privada a un archivo que un usuario seleccionó?

Desde File API, una URL para referencia de Blob y File , podemos aprender que:

Esta especificación define un esquema con URL del tipo:
blob: 550e8400-e29b-41d4-a716-446655440000 # sobre ABBA.

Estos se almacenan en un URL store (y los navegadores incluso deben tener su propio mini servidor HTTP a bordo para poder usar estas URL en css, img src e incluso XMLHttpRequest.

Uno puede crear esos Blob URL con:

  • var myBlobURL=window.URL.createFor(object); devuelve una Blob URL que se revoca automáticamente después de su primer uso.
  • var myBlobURL=window.URL.createObjectURL(object, flag_oneTimeOnly); devuelve una Blob URL reutilizable (a menos que flag_oneTImeOnly evalúe como verdadera) y puede revocarse con window.URL.revokeObjectURL(myBlobURL) .

Bingo, podría pensar ... sin embargo ... el URL Store solo se mantiene durante una sesión (por lo que sobrevivirá a una actualización de página, ya que sigue siendo la misma sesión) y se pierde cuando se descarga el documento.

Desde el MDN - Uso de URLs de objeto :

El objeto URL es una cadena que identifica el objeto Archivo. Cada vez que llamas a window.URL.createObjectURL (), se crea una URL de objeto única, incluso si ya has creado una URL de objeto para ese archivo. Cada uno de estos debe ser lanzado. Mientras se liberan automáticamente cuando el documento está descargado, si su página los usa dinámicamente, debe liberarlos explícitamente llamando a window.URL.revokeObjectURL ()

Eso significa que incluso cuando almacene la cadena Blob URL en una cookie o en un almacenamiento local persistente, ¡esa cadena sería inútil en una nueva sesión!

Eso debería llevarnos a un círculo completo y a la conclusión final:
No es posible (re) llenar un campo de entrada o un archivo seleccionado por el usuario (que no está en el área de "Almacenamiento local" de la zona de pruebas de los navegadores).
(A menos que obligue a sus usuarios a usar una versión desactualizada de Opera, o fuerce a sus usuarios a usar IE y algunos módulos / codificación activeX (implementando un selector de archivos personalizado), etc.)

Algunas lecturas adicionales:
http://www.cs.tut.fi/~jkorpela/forms/file.html
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
http://www.html5rocks.com/en/tutorials/file/filesystem/
http://www.html5rocks.com/en/tutorials/file/dndfiles/
http://caniuse.com/filereader
JavaScript: La guía definitiva - David Flanagan, capítulo-22: La api del sistema de archivos
¿Cómo guardar el resultado window.URL.createObjectURL () para uso futuro?
¿Cuánto tiempo persiste una Blob?
¿Cómo resolver el C: / fakepath?


Si todo lo que realmente desea es una persistencia temporal de los datos del archivo, como si hay un error en la página o si desea tener una página de confirmación, antes de enviar los datos a la base de datos, esto se hace comúnmente con la colocación los archivos en algún lugar temporales y manteniendo los datos en campos ocultos.

Esto no repobla el campo de entrada de archivo. Pero también puede simplemente tomar el nombre del archivo ingresado y colocarlo junto al cuadro de entrada del archivo.

al igual que:

<input type=hidden name="filename" value="<?php echo $filename; ?>" /> <input type="file" name="uploadfile" size="50" /> <?php if (!empty($filename)) echo $filename; ?>