2008-12-13 17 views
51

Digamos que tengo enlaces de descarga para archivos en mi sitio.comenzando la descarga del archivo con JavaScript

Al hacer clic en estos enlaces, envíe una solicitud AJAX al servidor que devuelve la URL con la ubicación del archivo.

Lo que quiero hacer es dirigir el navegador para descargar el archivo cuando la respuesta vuelva. ¿Hay una forma portátil de hacer esto?

Respuesta

2

Sugiero crear un iframe invisible en la página y configurar su isrc en la url que ha recibido del servidor - la descarga comenzará sin recargar la página.

O simplemente puede establecer el documento actual.location.href a la dirección url recibida. Pero eso puede hacer que el usuario vea un error si el documento solicitado en realidad no existe.

+0

cuándo será seguro retirar el iframe de el DOM? – AMember

3

A de acuerdo con los métodos mencionados por maxnk, pero es posible que desee reconsiderar intentar forzar automáticamente al navegador para que descargue la URL. Puede funcionar bien para archivos binarios pero para otros tipos de archivos (texto, PDF, imágenes, video), el navegador puede querer renderizarlo en la ventana (o IFRAME) en lugar de guardarlo en el disco.

Si realmente necesita hacer una llamada Ajax para obtener los enlaces de descarga finales, ¿qué pasa con el uso de DHTML para escribir dinámicamente el enlace de descarga (de la respuesta ajax) en la página? De esta forma, el usuario podría hacer clic en él para descargarlo (si es binario) o verlo en su navegador, o seleccionar "Guardar como" en el enlace para guardarlo en el disco. Es un clic adicional, pero el usuario tiene más control.

+0

Oh, sí, me olvidé de lo no binario, gracias. Como alternativa, puede ser una buena solución crear un contenedor alrededor de filestream desde el servidor y configurar el encabezado "Content-Disposition: attachment". Pero es adecuado solo para archivos pequeños. – maxnk

+0

O podría intentar engañar al navegador para que piense que el archivo descargable es binario haciendo que el servidor envíe el encabezado "Content-Type" como "application/octet-stream", sin importar el tipo de archivo real –

+1

Internet Explorer 8 bloquea la descarga de archivos usando todas las formas mencionadas (iframe, window.location.href, o incluso "http-equiv = REFRESH CONTENT"). "Para ayudar a proteger su seguridad, IE bloqueó este sitio para que no descargue archivos a su computadora ...". ¿Conoces una forma de suprimir esta advertencia y comenzar la descarga como, por ejemplo, Firefox do? He revisado varios sitios: Skype, Google (con Picasa) y Download.com provocan la advertencia al descargar (porque muestran una especie de página de agradecimiento). ¡Gracias por tu opinión! – Mar

1

Sugeriría window.open() para abrir una ventana emergente. Si se trata de una descarga, no habrá ventana y obtendrá su archivo. Si hay un 404 o algo así, el usuario lo verá en una nueva ventana (por lo tanto, su trabajo no será molestado, pero igual recibirán un mensaje de error).

+1

que funciona bien si se trata de un enlace al que se hace clic en el usuario (pero ¿por qué tiene que ser js?) Aún se encontrará con la violación de seguridad de IE. – Laith

10

Si esta es su propia aplicación de servidor, entonces se sugiere emplear el siguiente encabezado

Content-disposition: attachment; filename=fname.ext 

Esto obligará a cualquier navegador para descargar el archivo y no hacerla en la ventana del navegador.

7

Sólo tiene que llamar window.location.href = new_url de sus javascript y volverá a dirigir el navegador a esa URL, ya que el usuario ha escrito que en la barra de direcciones

+1

Tenía el mismo problema. Tu solución hizo el truco. ¡Gracias! –

26

Lo hacemos de esa manera: En primer lugar añadir este script.

<script type="text/javascript"> 
function populateIframe(id,path) 
{ 
    var ifrm = document.getElementById(id); 
    ifrm.src = "download.php?path="+path; 
} 
</script> 

lugar este donde desea que el botón de descarga (aquí usamos sólo un enlace):

<iframe id="frame1" style="display:none"></iframe> 
<a href="javascript:populateIframe('frame1','<?php echo $path; ?>')">download</a> 

El archivo 'download.php' (hay que poner en su servidor) contiene simplemente:

<?php 
    header("Content-Type: application/octet-stream"); 
    header("Content-Disposition: attachment; filename=".$_GET['path']); 
    readfile($_GET['path']); 
?> 

Así que cuando hace clic en el enlace, el iframe oculto a continuación, obtiene/abre la SourceFile 'download.php'. Con la ruta como parámetro get. ¡Creemos que esta es la mejor solución!

Cabe señalar que la parte PHP de esta solución es una demostración simple y potencialmente muy, muy inseguro. Le permite al usuario descargar cualquier archivo, no solo un conjunto predefinido.Eso significa que podrían descargar partes del código fuente del sitio en sí, posiblemente conteniendo credenciales de API etc.

+2

bien pero ... ¿cómo manejas los errores 404 de esta manera? –

+6

@Fabio: 'if (! File_exists ($ path)) {header (" HTTP/1.1 404 no encontrado "); salida; } 'debe hacer el truco –

+0

¿hay alguna forma de que javascript sepa el progreso de la descarga (o simplemente si está completo o no)? – lakenen

18

he creado un código abierto jQuery File Download plugin (Demo with examples) (GitHub) que también podría ayudar con su situación. Funciona de manera similar con un iframe, pero tiene algunas características geniales que he encontrado bastante útiles:

  • El usuario nunca sale de la misma página desde la que iniciaron la descarga de un archivo. Esta característica se está convirtiendo en fundamental para las aplicaciones web modernas
  • Probado soporte de navegadores (incluyendo móvil!)
  • Soporta POST y GET solicitudes de manera similar a la API AJAX de jQuery
  • funciones successCallback y failCallback permiten que seas explícita sobre lo que el usuario ve en cualquier situación
  • Junto con jQuery UI un desarrollador puede mostrar fácilmente un modal que le dice al usuario que se está descargando un archivo, disolver el modal después de que se inicia la descarga o incluso informar al usuario de manera amistosa que ha ocurrido un error Vea la demostración para ver un ejemplo de esto.
+0

Gracias jQuery File Download plugin es muy útil para mí .. –

+0

Hola. ¿Hay alguna manera de evitar que Internet Explorer 8 bloquee la descarga? La Barra de información de Internet Explorer solicita la confirmación de la descarga, lo que hace que la página se vuelva a cargar y no se descargue, con el resultado de que el usuario tiene que volver a hacer clic en el enlace de descarga. Gracias de antemano – Pierpaolo

+0

Esto es justo lo que necesitaba. ¡Gracias! – Tobia

14

Leyendo las respuestas, incluida la aceptada Me gustaría señalar las implicaciones de seguridad de pasar una ruta directamente a readfile a través de GET.

Puede parecer obvio para algunos, pero algunos pueden simplemente copiar/pegar este código:

<?php 
    header("Content-Type: application/octet-stream"); 
    header("Content-Disposition: attachment; filename=".$_GET['path']); 
    readfile($_GET['path']); 
?> 

Entonces, ¿qué ocurre si me pasa algo así como '/ ruta/a/fileWithSecrets' a este script? La secuencia de comandos proporcionada enviará con satisfacción cualquier archivo al que tenga acceso el usuario del servidor web.

Por favor refiérase a esta discusión para obtener información de cómo prevenir esto: How do I make sure a file path is within a given subdirectory?

+1

Enlace directo al archivo en el iframe y establezca los encabezados en un htaccess. –

1

Por qué estás haciendo las cosas del lado del servidor, cuando todo lo que necesita es para redirigir el navegador a diferentes window.location.href?

Aquí es código que analiza el archivo = cadena de consulta (taken from this question) y redirige el usuario a esa dirección en 1 segundo (funciona para mí, incluso en los navegadores de Android):?

<script type="text/javascript"> 
    var urlParams; 
    (window.onpopstate = function() { 
     var match, 
     pl = /\+/g, // Regex for replacing addition symbol with a space 
     search = /([^&=]+)=?([^&]*)/g, 
     decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); }, 
     query = window.location.search.substring(1); 

     urlParams = {}; 
     while (match = search.exec(query)) 
      urlParams[decode(match[1])] = decode(match[2]); 
    })(); 

    (window.onload = function() { 
     var path = urlParams["file"]; 
     setTimeout(function() { document.location.href = path; }, 1000); 
    }); 
</script> 

Si tiene jQuery en su proyecto definitivamente elimine esos manejadores window.onpopstate & window.onload y haga todo en $ (document) .ready (function() {});

3

Para evitar la falla de seguridad en la respuesta más votada, puede establecer el iframe src directamente en el archivo que desea (en lugar de un archivo php intermedio) y establecer la información del encabezado en un.archivo htaccess:

<Files *.apk> 
    ForceType application/force-download 
    Header set Content-Disposition attachment 
    Header set Content-Type application/vnd.android.package-archive 
    Header set Content-Transfer-Encoding binary 
</Files> 
2

En relación con la respuesta superior tengo una posible solución al riesgo de seguridad.

<?php 
    if(isset($_GET['path'])){ 
     if(in_array($_GET['path'], glob("*/*.*"))){ 
      header("Content-Type: application/octet-stream"); 
      header("Content-Disposition: attachment; filename=".$_GET['path']); 
      readfile($_GET['path']); 
     } 
    } 
?> 

Uso de la función glob() (he probado el archivo de descarga en un camino de una carpeta desde el archivo que desea descargar) que era capaz de hacer una serie rápida de los archivos que están "permitidos" para ser descargado y verifiqué la ruta pasada en su contra. Esto no solo asegura que el archivo que se está capturando no es algo sensible, sino que también verifica la existencia de archivos al mismo tiempo.

~ Nota: Javascript/HTML ~

HTML:

<iframe id="download" style="display:none"></iframe> 

y

<input type="submit" value="Download" onclick="ChangeSource('document_path');return false;"> 

JavaScript:

<script type="text/javascript"> 
    <!-- 
     function ChangeSource(path){ 
      document.getElementByID('download').src = 'path_to_php?path=' + document_path; 
     } 
    --> 
</script> 
+2

Esto es PHP .... Le preguntó a JavaScript: P –

+0

Correcto, pero esto se basaba en la respuesta principal. Sin embargo, agregaré una conexión JavaScript similar. –