-
Notifications
You must be signed in to change notification settings - Fork 229
Resumable file upload #375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
cb35c9f
fd0d199
aedfac6
3bc869e
e35f88b
26be945
df6e928
2b0efdd
5e37004
77e2aea
9780b28
ab7d0fa
f68b99a
a2d0718
5d70ced
63a328a
780a2f4
debf7ff
ee03926
57f4d9a
f5f5cf5
ee6ffc5
67618ae
3aa0884
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -1,82 +1,82 @@ | ||||||
| # Resumable file upload | ||||||
| # Carga de archivos reanudable | ||||||
|
|
||||||
| With `fetch` method it's fairly easy to upload a file. | ||||||
| Con el metodo `fetch` es bastante facil cargar un archivo. | ||||||
|
|
||||||
| How to resume the upload after lost connection? There's no built-in option for that, but we have the pieces to implement it. | ||||||
| ¿Como reanudar la carga de un archivo despues de perder la conección? No hay una opcion incorporada para eso, pero tenemos las piezas para implementarlo. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| Resumable uploads should come with upload progress indication, as we expect big files (if we may need to resume). So, as `fetch` doesn't allow to track upload progress, we'll use [XMLHttpRequest](info:xmlhttprequest). | ||||||
| Las cargas reanudables deberian venir con indicacion de progreso, ya que esperamos archivos grandes (Si necesitamos reanudar). Entonces, ya que `fetch` no permite rastrear el progreso de carga, usaremos [XMLHttpRequest](info:xmlhttprequest). | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| ## Not-so-useful progress event | ||||||
| ## Evento de progreso poco util | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| To resume upload, we need to know how much was uploaded till the connection was lost. | ||||||
| Para reanudar la carga, necesitamos saber cuanto fue cargado hasta la perdida de la coneccion. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| There's `xhr.upload.onprogress` to track upload progress. | ||||||
| Disponemos de `xhr.upload.onprogress` para rastrear el progreso de carga. | ||||||
|
|
||||||
| Unfortunately, it won't help us to resume the upload here, as it triggers when the data is *sent*, but was it received by the server? The browser doesn't know. | ||||||
| Desafortunadamente, esto no nos ayudara a reanudar la descarga, Ya que se origina cuando los datos son *enviados*, ¿pero fue recivida por el servidor? el navegador no lo sabe. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| Maybe it was buffered by a local network proxy, or maybe the remote server process just died and couldn't process them, or it was just lost in the middle and didn't reach the receiver. | ||||||
| Tal vez fue almacenada por un proxy de la red local, o quizá el proceso del servidor remoto solo murio y no pudo procesarla, o solo fue perdida en el medio y no alcanzo al receptor. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| That's why this event is only useful to show a nice progress bar. | ||||||
| Es por eso que este evento solo es útil para mostrar una barra de progreso bonita. | ||||||
|
|
||||||
| To resume upload, we need to know *exactly* the number of bytes received by the server. And only the server can tell that, so we'll make an additional request. | ||||||
| Para resumir una carga, necesitamos saber *exactamente* el numero de bytes recibidos por el servidor. Y eso solo lo sabe el servidor, por lo tanto haremos una solicitud adicional. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| ## Algorithm | ||||||
| ## Algoritmos | ||||||
|
|
||||||
| 1. First, create a file id, to uniquely identify the file we're going to upload: | ||||||
| 1. Primero, crear un archivo id, para unicamente identificar el archivo que vamos a subir: | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
| ```js | ||||||
| let fileId = file.name + '-' + file.size + '-' + +file.lastModifiedDate; | ||||||
| ``` | ||||||
| That's needed for resume upload, to tell the server what we're resuming. | ||||||
| Eso es necesario para reanudar la carga, para decirle al servidor lo que estamos reanudando. | ||||||
|
|
||||||
| If the name or the size or the last modification date changes, then there'll be another `fileId`. | ||||||
| Si el nombre o tamaño de la ultima fecha de modificacion cambia, entonces habrá otro `fileId`. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| 2. Send a request to the server, asking how many bytes it already has, like this: | ||||||
| 2. Envia una solicitud al servidor, preguntando cuantos bytes tiene, asi: | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
| ```js | ||||||
| let response = await fetch('status', { | ||||||
| headers: { | ||||||
| 'X-File-Id': fileId | ||||||
| } | ||||||
| }); | ||||||
|
|
||||||
| // The server has that many bytes | ||||||
| // El servidor tiene tanta cantidad de bytes | ||||||
| let startByte = +await response.text(); | ||||||
| ``` | ||||||
|
|
||||||
| This assumes that the server tracks file uploads by `X-File-Id` header. Should be implemented at server-side. | ||||||
| Esto asume que el servidor rastrea archivos cargados por el encabezado `X-File-Id`. Debe ser implementado en server-side. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| If the file doesn't yet exist at the server, then the server response should be `0` | ||||||
| Si el archivo no existe aun en el servidor, entonces su respuesta debe ser `0`. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| 3. Then, we can use `Blob` method `slice` to send the file from `startByte`: | ||||||
| 3. Entonces, podemos usar el metodo `Blob` `slice` para enviar el archivo desde `startByte`: | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
| ```js | ||||||
| xhr.open("POST", "upload", true); | ||||||
|
|
||||||
| // File id, so that the server knows which file we upload | ||||||
| // Archivo, de modo que el servidor sepa que archivo subimos | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
| xhr.setRequestHeader('X-File-Id', fileId); | ||||||
|
|
||||||
| // The byte we're resuming from, so the server knows we're resuming | ||||||
| // El byte desde el que estamos reanudando, asi el servidor sabe que estamos reanudando | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
| xhr.setRequestHeader('X-Start-Byte', startByte); | ||||||
|
|
||||||
| xhr.upload.onprogress = (e) => { | ||||||
| console.log(`Uploaded ${startByte + e.loaded} of ${startByte + e.total}`); | ||||||
| }; | ||||||
|
|
||||||
| // file can be from input.files[0] or another source | ||||||
| // El archivo puede ser de input.files[0] u otra fuente | ||||||
| xhr.send(file.slice(startByte)); | ||||||
| ``` | ||||||
|
|
||||||
| Here we send the server both file id as `X-File-Id`, so it knows which file we're uploading, and the starting byte as `X-Start-Byte`, so it knows we're not uploading it initially, but resuming. | ||||||
| Aqui enviamos al server ambos archivos id como `X-File-Id`, de modo que sepa que archivos estamos cargando, y el byte inicial como `X-Start-Byte`, de modo que sepa que no lo estamos cargando inicialmente, si no que reanudandolo. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ¿A quién queres más, a mamá o a papá? 😆 Corrijo desde la correcta corrección de Valentina. Tampoco está mal "de modo", creo que está poniendo algo más universal. Para no estar tan pegados al inglés "archivo id", "id del archivo" Mmm. Tampoco me gusta el original inglés "initially" Mmm. Se malinterpreta quiénes son "ambos": file-id, start-byte. No son "ambos archivos" Acentoss: acá estan ambas versiones de "que": "sino", el que opone "no ese sino otro" y las comas separando estructuras y qué tanto floreo, es confuso. Fuera. Debería mandar PR al inglés, el original es una porquería Uf, edité esto varias veces... AHORA se entiende.
Suggested change
|
||||||
|
|
||||||
| The server should check its records, and if there was an upload of that file, and the current uploaded size is exactly `X-Start-Byte`, then append the data to it. | ||||||
| El server deberia verificar sus registros, y en el caso de haber una carga de ese archivo, y el tamaño actual de la carga es exactamente The server should check its records, and if there was an upload of that file, and the current uploaded size is exactly `X-Start-Byte`, then append the data to it. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aquí te dejo otro dilema. Pero quiero que no te preocupes y sepas que a pesar de nuestras diferencias con Valentina nos seguimos queriendo. Pero no está mal (el anterior sí) es solo una sugerencia, ignorenla si les parece rebuscado. como purista, pero mejor más parecido al de uds, más claro.
Suggested change
|
||||||
|
|
||||||
|
|
||||||
| Here's the demo with both client and server code, written on Node.js. | ||||||
| Aqui esta la demo con el codigo tento del cliente como del servidor, escrito en Node.js. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| It works only partially on this site, as Node.js is behind another server named Nginx, that buffers uploads, passing them to Node.js when fully complete. | ||||||
| Esto funciona solo parcialmente en este sitio, ya que Node.js esta detras de otro servidor llamado Nginx, que almacena cargas, pasandolas a Node.js cuando esta completamente lleno. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| But you can download it and run locally for the full demonstration: | ||||||
| Pero puedes cargarlo y ejecutarlo localmente para la demostracion completa: | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| [codetabs src="upload-resume" height=200] | ||||||
|
|
||||||
| As we can see, modern networking methods are close to file managers in their capabilities -- control over headers, progress indicator, sending file parts, etc. | ||||||
| Como podemos ver, los metodos de networking modernos estan cerca de los gestores de archivos en sus capacidades -- control sobre header, indicador de progreso, enviar partes de archivos, etc. | ||||||
|
FroggyGentlemen marked this conversation as resolved.
Outdated
|
||||||
|
|
||||||
| We can implement resumable upload and much more. | ||||||
| Podemos implemetar la carga reanudable y mucho mas. | ||||||
Uh oh!
There was an error while loading. Please reload this page.