2012-11-09 14 views
9

Sto scrivendo una piccola applicazione node.js che riceve un POST multiparto da un modulo HTML e invia i dati in arrivo ad Amazon S3. Il modulo formidable fornisce lo multipart parsing, esponendo ciascuna parte come nodo Stream. Il modulo knox gestisce il PUT su s3.Segnalazione dell'avanzamento del caricamento da node.js

var form = new formidable.IncomingForm() 
, s3 = knox.createClient(conf); 

form.onPart = function(part) { 
    var put = s3.putStream(part, filename, headers, handleResponse); 
    put.on('progress', handleProgress); 
}; 

form.parse(req); 

che sto segnalato l'avanzamento del caricamento al client browser tramite socket.io, ma sto avendo difficoltà a ottenere questi numeri per riflettere il vero progresso del nodo da caricare s3.

Quando il browser per il caricamento del nodo avviene vicino all'istante, come accade quando il processo del nodo è in esecuzione sulla rete locale, l'indicatore di avanzamento raggiunge immediatamente il 100%. Se il file è di grandi dimensioni, ad esempio 300 MB, l'indicatore di avanzamento aumenta lentamente, ma è ancora più veloce di quanto consentirebbe la nostra larghezza di banda a monte. Dopo aver raggiunto il 100% di progresso, il client si blocca, presumibilmente in attesa del completamento del caricamento di s3.

Lo so putStream utilizza il metodo stream.pipe del Nodo internamente, ma non capisco il dettaglio di come funzioni realmente. La mia ipotesi è che il nodo divori i dati in entrata il più velocemente possibile, gettandolo in memoria. Se il flusso di scrittura è in grado di acquisire i dati abbastanza velocemente, i dati piccoli vengono conservati in memoria in una sola volta, poiché possono essere scritti e scartati. Se il flusso di scrittura è lento, come è qui, presumibilmente dobbiamo conservare tutti i dati in arrivo in memoria fino a quando non può essere scritto. Dal momento che stiamo ascoltando gli eventi data sullo stream di lettura per emettere progressi, finiamo per segnalare il caricamento come più veloce di quanto sia in realtà.

La mia comprensione di questo problema è vicino al marchio? Come potrei andare a ripararlo? Devo scendere e sporcarmi con write, drain e pause?

+0

stai segnalando i progressi al browser all'interno del handleProgress' callback '? Non hai pubblicato _any_ codice che potrebbe avere nulla a che fare con i report di avanzamento effettivi. Pubblicare più codice può essere d'aiuto. – lanzz

+0

Quale versione di Node.JS si sta utilizzando? Apperentemente [c'era un bug] (https://groups.google.com/forum/?fromgroups=#!topic/nodejs/pzhtOO6ePZ0) con 'request.pause()' (nel tuo caso: 'parte' variabile) in Node.JS v0.6.x, che è internamente usato da '.pipe()'. Questo dovrebbe essere corretto in v0.7 +. – freakish

+0

@lanzz: Esatto. L'effettiva implementazione non è davvero rilevante: ai fini della domanda potrebbe anche essere "console.log". – cantlin

risposta

7

Il tuo problema è che stream.pause isn't implemented on the part, che è un readstream molto semplice dell'output dal parser di modulo multipart.

Knox instructs the s3 request to emit "progress" events whenever the part emits "data". Tuttavia, poiché il flusso part ignora la pausa, gli eventi di avanzamento vengono emessi con la stessa velocità con cui i dati del modulo vengono caricati e analizzati.

Il formidabile form, tuttavia, sa come sia a pause e a resume (che inoltra le chiamate alla richiesta che sta analizzando).

Qualcosa del genere dovrebbe risolvere il tuo problema:

form.onPart = function(part) { 

    // once pause is implemented, the part will be able to throttle the speed 
    // of the incoming request 
    part.pause = function() { 
     form.pause(); 
    }; 

    // resume is the counterpart to pause, and will fire after the `put` emits 
    // "drain", letting us know that it's ok to start emitting "data" again 
    part.resume = function() { 
     form.resume(); 
    }; 

    var put = s3.putStream(part, filename, headers, handleResponse); 
    put.on('progress', handleProgress); 
}; 
+0

Grazie @ numbers1311407, ottima risposta. Sono obbligato a chiedere: riesci a vedere alcuni inconvenienti significativi nell'implementazione di '' pause'' e '' resume'' in questo modo? In effetti suppongo che renda il nostro server al massimo altrettanto reattivo come s3. L'ho implementato nel codice di test [qui] (https://github.com/cantlin/node-s3-proxy). – cantlin

+0

Dato che io non sono un mago I/O, tendo a chiedermi la stessa cosa. Ma la [pagina doc del file node.js] (http://nodejs.org/api/http.html#http_request_pause) menziona la limitazione del caricamento come caso utile per 'pause'. [Questa discussione sui newsgroup sulla request.pause "bug"] (https://groups.google.com/forum/#!msg/nodejs/yv6Dl-O-wYk/qPAKqKDDT9gJ) merita di essere esaminata (i commenti di Mikeal e Marco). – numbers1311407

+0

Alla fine risolve due problemi: 1.) mantiene il client in linea fino al completamento del caricamento effettivo e 2.) consente che ciò accada senza buffering di grandi quantità di dati sul server. È possibile risolvere anche questo problema connettendo un flusso bufferizzato prima della richiesta s3, monitorando i progressi e richiamando il client al termine del caricamento. Ma questo tira fuori # 2. – numbers1311407