javascript - para - Detectar cuando el usuario acepta descargar un archivo
html boton descargar archivo (6)
Quiero redirigir al usuario a una página web diferente después de hacer clic en un hipervínculo que les permite descargar un archivo. Sin embargo, dado que necesitan hacer una elección en el cuadro de diálogo abrir / guardar archivo, no quiero redirigirlos hasta que acepten la descarga.
¿Cómo puedo detectar que realizaron esta acción?
@dandavis
¡Pagaré con alegría 10 veces esta recompensa si alguien puede demostrar una forma mecánica de determinar la acción del usuario posterior a la solicitud en cualquier archivo!
¿Estás listo? porque aquí hay una solución hacky 😉
My
StreamSaver
lib no usa blob para descargar un archivo con
a[download]
.
Utiliza el trabajador de servicio para transmitir algo al disco emulando cómo el servidor maneja la descarga con el encabezado del archivo adjunto de disposición de contenido.
evt.respondWith(
new Response(
new ReadableStream({...})
)
)
Ahora no tiene ninguna forma exacta de saber qué presionó el usuario en el cuadro de diálogo, pero tiene alguna información sobre la transmisión.
Si el usuario presiona cancelar en el cuadro de diálogo o cancelar la descarga en curso, la transmisión también se cancela.
El botón guardar es más complicado. Pero comencemos con lo que un cubo de corriente highWaterMark puede decirnos.
En mi
ejemplo de torrent
, registro el
writer.desiredSize
.
Es la correlación con la cantidad de datos que está dispuesto a recibir.
Cuando escribe algo en la secuencia, reducirá el tamaño deseado (ya sea una estrategia de conteo o byte).
Si nunca aumenta, significa que el usuario probablemente haya detenido la descarga.
Cuando cae por debajo de 0, está escribiendo más datos de los que solicita el usuario.
Y cada parte que escribes devuelve una promesa
writer.getWriter().write(uint8).then(() => {
// Chunk have been sent to the destination bucket
// and desiredSize increase again
})
Esa promesa se resolverá cuando el cubo no esté lleno. Pero no significa que el fragmento se haya escrito en el disco todavía, solo significa que el fragmento se ha pasado de una secuencia a otra secuencia (de escritura -> legible -> responder con) y a menudo lo hará al principio de la transmisión y cuando otro fragmento anterior se ha escrito en el disco.
Es posible que la secuencia de escritura pueda finalizar incluso antes de que el usuario elija si los datos del agujero pueden caber dentro del depósito (memoria)
ajustar el tamaño del cubo para que sea menor, entonces los datos pueden ayudar
Por lo tanto, puede suponer cuándo la descarga comienza, finaliza y se detiene, pero no lo sabrá con certeza ya que no obtiene ningún evento (aparte del aborto que cierra la transmisión)
Tenga en cuenta que el ejemplo de torrent no muestra el tamaño correcto si no tiene soporte para transmisiones transferibles, pero podría evitar esto si hace todo dentro de un trabajador de servicio. (en lugar de hacerlo en el hilo principal)
detectar cuándo finaliza la transmisión es tan fácil como
readableStream.pipeTo(fileStream).then(done)
Y para futuras referencias, WICG/native-file-system podría darle acceso para escribir archivos en el disco, pero tiene que resolver una promesa de diálogo rápido antes de que pueda continuar y puede ser justo lo que el usuario solicita
Hay ejemplos de guardar un blob como flujo, e incluso más blobs múltiples como zip también si estás interesado
Como descubrí durante años de mantenimiento de download.js , simplemente no hay forma de saber de JS (o probablemente en general, ver más abajo) lo que un usuario elige hacer con el diálogo Abrir / Guardar descarga. Es una solicitud de función común, y la he examinado repetidamente a lo largo de los años. Puedo decir con confianza que es imposible; ¡Pagaré con alegría 10 veces esta recompensa si alguien puede demostrar una forma mecánica de determinar la acción del usuario posterior a la solicitud en cualquier archivo!
Además, no se trata solo de reglas JS, el problema se complica por la forma en que los navegadores descargan y solicitan dichos archivos. Esto significa que incluso los servidores no siempre pueden decir lo que sucedió. Puede haber algunas soluciones específicas para algunos casos específicos, pero no son bonitas o simples.
Puede obligar a sus usuarios a "volver a cargar" un archivo descargado con un
<input type=file>
para validarlo, pero eso es engorroso en el mejor de los casos, y el diálogo de búsqueda de archivos locales puede ser alarmante para algunos.
Es el único método seguro para garantizar una descarga, pero para aplicaciones no sensibles es muy draconiano, y no funcionará en algunas plataformas "móviles" que carecen de soporte de archivos.
También puede intentar ver desde el lado del servidor, enviando un mensaje al cliente de que el archivo fue alcanzado en el servidor. El problema aquí es que las descargas comienzan a descargarse tan pronto como aparece el cuadro de diálogo Abrir / Guardar, aunque de manera invisible en el fondo. Es por eso que si espera unos momentos para "aceptar" un archivo grande, parece que se descarga rápidamente al principio. Desde la perspectiva del servidor, la actividad es la misma independientemente de lo que haga el usuario.
Para un archivo enorme, probablemente podría detectar que no se transfirió todo el archivo, lo que implica que el usuario hizo clic en "cancelar", pero es un procedimiento de sincronización complicado que lleva el estado del backend al cliente. Se requeriría una gran cantidad de programación personalizada con sockets, paso de mensajes PHP, EventSource, etc. para obtener poca ganancia. También es una carrera contra el tiempo, y una cantidad de tiempo incierta; y ralentizar la descarga no se recomienda para la satisfacción del usuario.
Si es un archivo pequeño, se descargará físicamente antes de que el usuario vea el diálogo, por lo que el servidor será inútil. También considere que algunas extensiones del administrador de descargas se hacen cargo del trabajo, y no se garantiza que se comporten de la misma manera que un navegador estándar. Forzar una espera puede ser traicionero para alguien con un disco duro lento que tarda "para siempre" en "terminar" una descarga; Todos hemos experimentado esto, y no poder continuar mientras el "spinny" se apaga reduciría la satisfacción del usuario, por decirlo suavemente.
En resumen, no hay una manera simple, y realmente no hay manera en general, excepto por los archivos enormes que sabes que tomarán mucho tiempo para descargar. He gastado mucho sudor de sangre y lágrimas tratando de proporcionar a mis usuarios de download.js la capacidad, pero simplemente no hay buenas opciones. Ryan dahl inicialmente escribió node.js para poder proporcionar a sus usuarios una barra de progreso de carga, tal vez alguien haga un paquete de servidor / cliente para que sea más fácil hacer lo mismo para las descargas.
Dado que el usuario es, o debe ser consciente de que el archivo debe descargarse antes del siguiente paso en el proceso, el usuario debe esperar alguna forma de confirmación de que el archivo se ha descargado.
Puede crear un identificador único o marca de tiempo para incluir dentro del nombre del archivo descargado utilizando el elemento
<a>
con el atributo de
download
establecido en el nombre del archivo modificado.
Al
click
evento del elemento
<button>
, llame a
.click()
en el elemento
<a>
con
href
establecido en un
Blob URL
del archivo.
En
a
elemento,
click
controlador, llame a
.click()
en un elemento
<input type="file">
, donde en el evento de
change
adjunto, el usuario debe seleccionar el mismo archivo que se descargó en la acción del usuario que inició la descarga del archivo.
Observe el encadenamiento de llamadas a
.click()
comienza con la acción del usuario.
Consulte
Activación, haga clic en input = file en asincrónico ajax done ()
.
Si el archivo seleccionado del sistema de archivos del usuario es igual al nombre del archivo descargado modificado, llame a la función, de lo contrario notifique al usuario que la descarga del archivo no ha sido confirmada.
window.addEventListener("load", function() {
let id, filename, url, file;
let confirmed = false;
const a = document.querySelector("a");
const button = document.querySelector("button");
const confirm = document.querySelector("input[type=file]");
const label = document.querySelector("label");
function confirmDownload(filename) {
if (confirmed) {
filename = filename.replace(/(-/d+)/, "");
label.innerHTML = "download of " + filename + " confirmed";
} else {
confirmed = false;
label.innerHTML = "download not confirmed";
}
URL.revokeObjectURL(url);
id = url = filename = void 0;
if (!file.isClosed) {
file.close()
}
}
function handleAnchor(event) {
confirm.click();
label.innerHTML = "";
confirm.value = "";
window.addEventListener("focus", handleCancelledDownloadConfirmation);
}
function handleFile(event) {
if (confirm.files.length && confirm.files[0].name === filename) {
confirmed = true;
} else {
confirmed = false;
}
confirmDownload(filename);
}
function handleDownload(event) {
// file
file = new File(["abc"], "file.txt", {
type: "text/plain",
lastModified: new Date().getTime()
});
id = new Date().getTime();
filename = file.name.match(/[^.]+/g);
filename = filename.slice(0, filename.length - 1).join("")
.concat("-", id, ".", filename[filename.length - 1]);
file = new File([file], filename, {
type: file.type,
lastModified: id
});
a.download = filename;
url = URL.createObjectURL(file);
a.href = url;
alert("confirm download after saving file");
a.click();
}
function handleCancelledDownloadConfirmation(event) {
if (confirmed === false && !confirm.files.length) {
confirmDownload(filename);
}
window.removeEventListener("focus", handleCancelledDownloadConfirmation);
}
a.addEventListener("click", handleAnchor);
confirm.addEventListener("change", handleFile);
button.addEventListener("click", handleDownload);
});
<button>download file</button>
<a hidden>download file</a>
<input type="file" hidden/>
<label></label>
Tuve un proyecto en el que incursioné recientemente que requería que especificara si un usuario podía cargar un tipo particular de archivo, es decir (un usuario puede cargar un png pero no un pdf).
Es posible que no haya utilizado el método más eficiente, pero en última instancia, lo que hice fue codificar una pequeña "aplicación web" incorporada que funcionaba como un explorador de archivos, para cargarla o descargarla.
Supongo que el ejemplo más cercano sin lanzar mi "proyecto secreto" sería https://encodable.com/filechucker/
Tal vez podría escribir un explorador de archivos integrado simple como el que los servicios en la nube usan a veces (es decir, Dropbox) y tener algunas funciones que detectan la entrada con cuadros personalizados y otras cosas.
Solo unos pocos pensamientos.
jquery.fileDownload le permite hacer esto:
$(document).on("click", "a.fileDownloadPromise", function () {
$.fileDownload($(this).prop(''href''))
.done(function () { alert(''File download a success!''); })
.fail(function () { alert(''File download failed!''); });
return false;
});
Echa un vistazo a Github:
prueba esto:
<html>
<head>
<script type="text/javascript">
function Confirmation(pg) {
var res = confirm("Do you want to download?");
if(res){
window.open(pg,"_blank");
}
return res;
}
</script>
</head>
<body>
<a href="http://yoursite/download.zip" onclick="return Confirmation(''http://yoursite/redirect.html'');">download</a>
</body>
</html>