Pregunta

Recientemente he contribuido con algún código a Moodle, que utiliza algunas de las capacidades de HTML5 para permitir que los archivos se carguen en formularios a través de arrastrar y soltar desde el escritorio (la parte central del código está aquí: https://github.com/moodle/moodle/blob/master/lib/form/dndupload.js para referencia).

Esto funciona bien, excepto cuando un usuario drags a carpeta / directorio en lugar de un archivo real. Luego se carga la basura en el servidor, pero con el nombre de archivo que coincide con la carpeta.

Lo que estoy buscando es una forma fácil y confiable de detectar la presencia de un carpeta en el Filelista Objeto, por lo que puedo omitirlo (y probablemente también devolver un mensaje de error amistoso).

He revisado la documentación en MDN, así como una búsqueda web más general, pero no he presentado nada. También he revisado los datos en las herramientas de desarrollador de Chrome y parece que el 'escribe' del objeto de archivo se establece consistentemente en "" para carpetas. Sin embargo, no estoy muy convencido de que este sea el método de detección de navegador cruzado más confiable.

¿Alguien tiene más sugerencias?

¿Fue útil?

Solución

No puedes confiar en file.type. Un archivo sin una extensión tendrá un tipo de "". Guarde un archivo de texto con un .jpg extensión y cargarlo en un control de archivo, y su tipo se mostrará como image/jpeg. Y, una carpeta llamada "SomeFolder.jpg" también tendrá su tipo como image/jpeg.

Intente leer el archivo con un FileReader. Si se arrastra un directorio, el FileReader elevará el error evento:

var reader = new FileReader();
reader.onload = function (e) {
    // it's a file
};
reader.onerror = function (e) {
    // it's a directory
};
reader.readAsText(file);

Felizmente, en ie11, cuando se deja caer un directorio, el e.dataTransfer.files La colección está vacía.

Chrome expone una propiedad adicional en e.dataTransfer llamó items que contiene una colección de DataTransferItem objetos. En cada uno de estos objetos, puede llamar item.webkitGetAsEntry(), que devuelve un Entry objeto. los Entry El objeto tiene propiedades isDirectory y isFile:

// Chrome only
if (e.dataTransfer.items && e.dataTransfer.items.length) {
   [].forEach.call(e.dataTransfer.items, function(item) {
      var entry = item.webkitGetAsEntry();
      if (entry && entry.isFile) {
         var file = item.getAsFile(); // same as object in e.dataTransfer.files[]
         // do something with the file
      }
   }
}

Curiosamente, en mi experimentación, cada carpeta que he visto ha tenido su File.size % 4096 como cero. Sin embargo, por supuesto, algunos archivos también lo tendrán. File.size no es un indicador confiable de si un archivo es realmente una carpeta.

Otros consejos

También me encontré con este problema y a continuación está mi solución. Básicamente, tuve un enfoque de dos puntas:

(1) Compruebe si el tamaño del objeto del archivo es grande y considere que es un archivo genuino si es más de 1 MB (supongo que las carpetas en sí no son tan grandes).(2) Si el objeto de archivo es menor que 1MB, lo leí usando el método 'ReadAsArrayBuffer' de FileReader. Lecturas exitosas Call 'Onload' y creo que esto indica que el objeto de archivo es un archivo genuino. Las lecturas fallidas llaman a 'onError' y lo considero un directorio. Aquí está el código:

var isLikelyFile = null;
if (f.size > 1048576){ isLikelyFile = false; }
else{
    var reader = new FileReader();
    reader.onload = function (result) { isLikelyFile = true; };
    reader.onerror = function(){ isLikelyFile = false; };
    reader.readAsArrayBuffer(f);
}
//wait for reader to finish : should be quick as file size is < 1MB ;-)
var interval = setInterval(function() {
    if (isLikelyFile != null){
        clearInterval(interval);
        console.log('finished checking File object. isLikelyFile = ' + isLikelyFile);
    }
}, 100);

Probé esto en FF 26, Chrome 31 y Safari 6 y tres navegadores llaman 'onerror' al intentar leer directorios. Avíseme si alguien puede pensar en un caso de uso donde esto falle.

Propongo llamar FileReader.readAsBinaryString sobre el File objeto. En Firefox, esto aumentará una excepción cuando el File es un Directory. Solo hago esto si el File cumple con las condiciones propuestas por Gilly3.

Por favor vea mi publicación de blog en http://hs2n.wordpress.com/2012/08/13/detecting-folders-in-html-drop-area/ para más detalles.

Además, la versión 21 de Google Chrome ahora es compatible con las carpetas de soltar. Puede verificar fácilmente si los elementos caídos son carpetas y también leer su contenido.

Desafortunadamente, no tengo ninguna solución (del lado del cliente) para versiones cromadas más antiguas.

Otra nota es que ese tipo es "" para cualquier archivo que tenga una extensión desconocida. Intente descargar un archivo llamado Test.blah y el tipo estará vacío. Y ... intente arrastrar y soltar una carpeta llamada Test.jpg - El tipo se establecerá en "Image/JPEG". Para ser 100% correcto, no puede depender del tipo únicamente (o si es así, realmente).

En mis pruebas, las carpetas siempre han sido de tamaño 0 (en FF y Chrome en Windows 7 y debajo de Linux Mint de 64 bits (Ubuntu esencialmente). Por lo tanto, mi verificación de carpetas es simplemente verificar si el tamaño es 0 y parece que funciona para mí En nuestro entorno. Tampoco queremos que los archivos de 0 byte se carguen, por lo que si es 0 byte, el mensaje vuelve como "omitido - 0 bytes (o carpeta)"

Para su información, esta publicación le dirá cómo usar la API DataTransfer en Chrome para detectar el tipo de archivo: http://updates.html5rocks.com/2012/07/drag-and-drop-a-folder-onto-chrome-now-avilable

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top