2012-08-25 8 views
14

Tengo un sitio web con una carga de archivo normal <input type="file">, POSTING los datos al backend cuando se envía el formulario.¿Cargar archivo arrastrando y soltando sin AJAX, sincrónicamente en primer plano?

Me gustaría mejorar progresivamente el formulario para que pueda colocar un archivo desde fuera del navegador en cualquier parte de la ventana gráfica (no solo en el campo de entrada del archivo, como está incorporado en algunos navegadores) para cargarlo.

Si el formulario no se envía automáticamente no es importante. Entonces, si arrastrar y soltar solo selecciona el archivo en el campo del archivo, sin iniciar una carga, está bien. No necesito soporte para múltiples archivos. No es necesario que muestre el progreso de la carga, las miniaturas o cualquier otra cosa elegante.

Sé que hay libs de JS que soportan cargas de arrastrar y soltar, pero todas parecen cargarse a través de AJAX. Podría hacer eso, pero luego tendría que modificar el backend y el frontend para manejar los errores de carga, redirigir y mostrar los mensajes correctos en caso de éxito, y así sucesivamente.

Quiero una mejora progresiva que no requiera ningún cambio de back-end. Debería ocurrir de forma síncrona utilizando el formulario en la página. JS está bien, siempre que la carga suceda "en primer plano". El AJAX sincrónico no funcionaría, por supuesto.

+1

En Chrome, al menos, puede configurar los archivos de una entrada en el archivo de caída: http://jsfiddle.net/qMmPr/. ¿Es eso lo que buscas? – pimvdb

+0

@pimvdb ¡Eso es hermoso! Exactamente lo que quería. Y olvidé mencionar que como esto es para un servicio interno, solo Chrome está bien. Si escribe eso como respuesta (además del comentario), lo marcaré como aceptado. –

Respuesta

15

Aunque en realidad no es "síncrono" (la ejecución de JavaScript no se detendrá), puede establecer los archivos seleccionados por <input type="file"> programáticamente. De hecho, tales elementos y el arrastre comparten su implementación de backend de archivos (instancias File y FileList), por lo que es muy directo. Además, debido a que ambas interfaces usan FileList s, arrastrar varios archivos funciona igual de bien.

Esto funciona en Chrome (usando jQuery): http://jsfiddle.net/qMmPr/.

$(document).on("dragover drop", function(e) { 
    e.preventDefault(); // allow dropping and don't navigate to file on drop 
}).on("drop", function(e) { 
    $("input[type='file']") 
     .prop("files", e.originalEvent.dataTransfer.files) // put files into element 
     .closest("form") 
      .submit(); // autosubmit as well 
}); 
+0

@Henrik N: ahora me doy cuenta de que no le gustan los archivos '.lnk' en Windows.Al seleccionarlos a través del elemento de entrada, se selecciona el archivo al que se vincula el acceso directo, mientras que al soltar un archivo '.lnk' se obtiene el archivo' .lnk'. – pimvdb

+0

No es un problema para mi caso de uso. ¡Gracias de nuevo por esto! –

+3

alguna idea en una solución multiplataforma? (acaba de probar que prop() en FF16: no funciona) –

-1

Se puede hacer girando AutoUpload en false, la recogida de los archivos en una matriz, a continuación, en el formulario de envío hacer una sola llamada ajax con todos los archivos junto con los datos del formulario, como se describe here.

+0

Gracias por esto, pero tenga en cuenta "sin Ajax" en la pregunta :) –

+0

Cierto, mi enfoque todavía usa ajax, pero es solo una solicitud y de tal manera que aparece como una operación de envío normal para el usuario. – Tor

+0

Sí, definitivamente es una buena solución en muchos casos. Pero como la pregunta lo menciona, hacerlo a través de Ajax significa que necesita un manejo especial de errores, y así sucesivamente. No es lo que buscaba en este caso, pero agradezco la entrada de todos modos :) –

0

Gracias a @pimvdb comment, se me ocurrió una solución bastante elegante.

Como arrastrar y soltar en el <input type="file" /> funciona, ¿por qué no hacerlo en pantalla completa en dragstart para asegurarse de que el usuario no se lo puede perder? De todos modos él está arrastrando así que sus intenciones son claras en este momento.

He aquí una demostración: https://jsfiddle.net/08wbo4um

NB: por desgracia esto no parece funcionar en un iframe, pero funciona en una página real. Aún puedes aprehender el comportamiento.

Aquí está el fragmento:

$('input[type="file"]').on('change', function(e){ 
 
    var fileName = e.target.files[0].name; 
 
    if (fileName) { 
 
     $(e.target).parent().attr('data-message', fileName); 
 
    } 
 
    }); 
 
    
 
    $(document).on('drag dragstart dragend dragover dragenter dragleave drop', function(e) { 
 
    if ($('input[type="file"]').length) { 
 
     if (['dragover', 'dragenter'].indexOf(e.type) > -1) { 
 
     if (window.dragTimeout) 
 
      clearTimeout(window.dragTimeout); 
 
     $('body').addClass('dragged'); 
 
     } else if (['dragleave', 'drop'].indexOf(e.type) > -1) { 
 
     // Without the timeout, some dragleave events are triggered 
 
     // when the :after appears, making it blink... 
 
     window.dragTimeout = setTimeout(function() { 
 
      $('body').removeClass('dragged'); 
 
     }, 100); 
 
     } 
 
    } 
 
    });
h3, p { 
 
    text-align: center; 
 
} 
 

 
.form-group { 
 
    margin: 30px; 
 
} 
 

 
.file-upload .form-control { 
 
    height: 150px; 
 
    outline: 1px dashed #ccc; 
 
    outline-offset: -15px; 
 
    background-color: #eee; 
 
} 
 
.file-upload .form-control:before { 
 
    content: "\f093"; 
 
    font: normal normal normal 14px/1 FontAwesome; 
 
    font-size: 3em; 
 
    left: 0; 
 
    right: 0; 
 
    display: block; 
 
    margin: 20px auto; 
 
    text-align: center; 
 
} 
 
.file-upload .form-control:after { 
 
    content: attr(data-message); 
 
    left: 0; 
 
    right: 0; 
 
    bottom: 0; 
 
    text-align: center; 
 
    display: block; 
 
} 
 
.file-upload .form-control input[type="file"] { 
 
    cursor: pointer; 
 
    opacity: 0; 
 
    width: 100%; 
 
    height: 100%; 
 
    position: absolute; 
 
    top: 0; 
 
    bottom: 0; 
 
    right: 0; 
 
    left: 0; 
 
} 
 
body.dragged .file-upload .form-control input[type="file"] { 
 
    /* Make sure it is full screen, whatever the position absolute container */ 
 
    position: fixed; 
 
    top: -50vh; 
 
    bottom: -50vh; 
 
    left: -50vw; 
 
    right: -50vw; 
 
    height: 200vh; 
 
    width: 200vw; 
 
    z-index: 10002; 
 
} 
 

 
body:after { 
 
    content: 'You can drop the file. :-)'; 
 
    font-size: 2em; 
 
    text-align: center; 
 
    line-height: 100vh; 
 
    position: absolute; 
 
    top: 10px; 
 
    bottom: 10px; 
 
    left: 10px; 
 
    right: 10px; 
 
    background-color: #eee; 
 
    z-index: 10000; 
 
    border-radius: 4px; 
 
    border: thin solid #ccc; 
 
    visibility: hidden; 
 
    opacity: 0; 
 
    transition: visibility 0s, opacity 0.5s ease; 
 
} 
 

 
body.dragged:after { 
 
    opacity: 1; 
 
    visibility: visible; 
 
}
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/> 
 
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/> 
 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script> 
 

 
<h3>Drag N Drop file upload without AJAX Demo</h3> 
 
<p>Try drag and dropping a file. :-)</p> 
 

 
<div class="form-group file-upload" required="required"> 
 
    <label class="cols-sm-2 control-label" for="document_file">File Upload</label><br> 
 
    <div class="cols-sm-10"> 
 
     <div class="input-group"> 
 
     <span class="input-group-addon"><i class="fa fa-file" aria-hidden="true"></i></span> 
 
     <div class="form-control" data-message="Click to select file or drag n drop it here"> 
 
      <input required="required" title="Click to select file or drag n drop it here" type="file" name="document[file]" id="document_file"> 
 
     </div> 
 
     </div> 
 
    </div> 
 
    </div>

Cuestiones relacionadas