2012-01-13 9 views
8

Recientemente he contribuido con algunos códigos a Moodle que usa algunas de las capacidades de HTML5 para permitir que los archivos se carguen en formularios mediante arrastrar y soltar desde el escritorio (la parte central del código es aquí: https://github.com/moodle/moodle/blob/master/lib/form/dndupload.js para referencia).Detectando carpetas/directorios en objetos Javascript FileList

Esto está funcionando bien, a excepción de cuando un usuario arrastra un carpeta/directorio lugar de un archivo real. La basura luego se carga en el servidor, pero con el nombre de archivo que coincide con la carpeta.

Lo que estoy buscando es una manera fácil y confiable para detectar la presencia de una carpeta en el objeto listaDeArchivos, por lo que puede saltar (y probablemente devolverá un mensaje de error también amable).

He revisado la documentación en MDN, así como una búsqueda web más general, pero no encontré nada. También he examinado los datos en las herramientas de desarrollo de Chrome y parece que el 'tipo' del objeto Archivo se establece sistemáticamente en "" para las carpetas. Sin embargo, no estoy del todo convencido de que este sea el método de detección más confiable entre navegadores.

¿Alguien tiene alguna sugerencia mejor?

Respuesta

17

No puede confiar en file.type. Un archivo sin una extensión tendrá un tipo de "". Guarde un archivo de texto con una extensión .jpg y cárguelo 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 un directorio es arrastrado en el FileReader elevará el caso error:

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 elimina un directorio, la colección e.dataTransfer.files está vacía.

Chrome expone una propiedad adicional en e.dataTransfer llamada items que contiene una colección de objetos DataTransferItem. En cada uno de estos objetos, puede llamar al item.webkitGetAsEntry(), que devuelve un objeto Entry. El objeto tiene propiedades EntryisDirectory 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, todas las carpetas que he mirado ha tenido sus File.size % 4096 como cero. Sin embargo, por supuesto, algunos archivos también tendrán eso. File.size no es un indicador confiable de si un archivo es realmente una carpeta.

+0

Gracias por la sugerencia - Me preguntaba si rechazar los archivos de tamaño cero sería un buen punto de partida (este parece ser el tamaño de las carpetas en Windows) y luego usar FileReader para verificar si el contenido es \ 0 cuando File.size % 4096 es cero – davosmith

+0

Absolutamente. El descubrimiento más sorprendente para mí fue que el tamaño no era consistentemente 0 para las carpetas. La cosa '% 4096' es interesante. Ciertamente, se necesita más investigación. Me gusta, ¿es universal 4096 o es específico para Windows 7 de 64 bits? – gilly3

+0

Impar: obtuve el tamaño 0 de forma consistente con Win 7 (64 bits) (lo verificaré dos veces, pero lo intenté con Chrome/Firefox). Sin embargo, obtuve 4.096 bytes (exactamente) con Ubuntu 11.10 (64 bits) (nuevamente con Chrome/Firefox). – davosmith

2

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

Consulte la publicación de mi blog en http://hs2n.wordpress.com/2012/08/13/detecting-folders-in-html-drop-area/ para obtener más información.

Además, la versión 21 de Google Chrome ahora admite la eliminación de carpetas. Puede verificar fácilmente si los elementos descartados son carpetas y también leer sus contenidos.

Por desgracia, no disponemos de solución (del lado del cliente) para las versiones anteriores de Chrome.

+0

Tenga en cuenta que los criterios de tamaño propuestos por gilly3 no son válidos, vea mi comentario en su respuesta. –

0

Otra nota es que el tipo es "" para cualquier archivo que tenga una extensión desconocida. Intente cargar un archivo llamado test.blah y el tipo estará vacío. Y ... intenta arrastrar y soltar una carpeta llamada test.jpg - type se establecerá en "image/jpeg". Para ser 100% correcto, no puede depender únicamente del tipo (o si realmente lo hace).

En mi prueba, las carpetas siempre han sido de tamaño 0 (en FF y Chrome en Windows 7 de 64 bits y bajo Linux Mint (Ubuntu esencialmente). Por lo tanto, mi comprobación de carpeta es solo comprobar si el tamaño es 0 y parece para que trabaje para mí en nuestro entorno. Tampoco queremos archivos de 0 bytes cargados tampoco, así que si es de 0 bytes el mensaje vuelve como "omitido - 0 bytes (o carpeta)"

6

también me encontré con este problema y a continuación es mi solución Básicamente, lo que llevó a tener un enfoque doble:.

(1) compruebe si el tamaño del objeto File es grande, y considérelo como un archivo genuino si es más de 1MB (supongo que las carpetas nunca son tan grandes). (2) Si el objeto del archivo es menor de 1 MB, entonces yo lo leen utilizando el método de FileReader 'readAsArrayBuffer'. La lectura exitosa llama 'onload' y creo que esto indica que el objeto de archivo es un archivo genuino. Las lecturas fallidas llaman '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); 

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

+0

Buen enfoque. Excepto la magia negra con setInterval, que considero propenso a errores. Será mejor que use el sistema de devolución de llamada asincrónico y deje que el lector finalice con éxito o fracaso. También su readAsArrayBuffer() pierde el parámetro. –

+0

Buena captura con readAsArrayBuffer: actualicé el código para reflejar la corrección de errores (y para corregir otro error: isLikelyFile ahora es falso si el tamaño del archivo es> 1 MB). No he tenido problemas con setInterval, pero es bueno tenerlo en cuenta. –

+0

Tenga en cuenta que la optimización '(f.size> 1048576)' en algunos casos será problemática, ya que el tamaño de la carpeta puede ser mayor que eso. Para una carpeta con una gran cantidad de archivos, verifiqué el tamaño de mi carpeta temporal (Windows 7), y son 2883584 bytes ... – Daryn

Cuestiones relacionadas