2016-04-24 3 views
1

Ich bin dabei, eine Dateiuploadkomponente zu erstellen, mit der Sie Dateiuploads anhalten/fortsetzen können. Der Standard Weg dies zu erreichen scheint zu sein, die Datei in Stücke auf dem Client-Rechner zu zerlegen, dann die Stücke zusammen mit Buchhaltungsinformationen an den Server zu senden, der die Stücke in einem Bereitstellungsverzeichnis speichern und dann zusammenführen kann sie zusammen, wenn es alle Stücke erhalten hat. Also, das mache ich.Stoppfunktion wird mehrmals aufgerufen

Ich benutze Node/Express und ich bin in der Lage, die Dateien zu bekommen, aber ich bin in ein Problem, weil meine merge_chunks Funktion mehrmals aufgerufen wird.

Hier ist mein Call-Stack:

router.post('/api/videos', 
    upload.single('file'), 
    validate_params, 
    rename_uploaded_chunk, 
    check_completion_status, 
    merge_chunks, 
    record_upload_date, 
    videos.update, 
    send_completion_notice 
); 

die check_completion_status Funktion wie folgt implementiert:

/* Recursively check to see if we have every chunk of a file */ 
var check_completion_status = function (req, res, next) { 
    var current_chunk = 1; 
    var see_if_chunks_exist = function() { 
    fs.exists(get_chunk_file_name(current_chunk, req.file_id), function (exists) { 
     if (current_chunk > req.total_chunks) { 
     next(); 
     } else if (exists) { 
     current_chunk ++; 
     see_if_chunks_exist(); 
     } else { 
     res.sendStatus(202); 
     } 
    }); 
    }; 
    see_if_chunks_exist(); 
}; 

die Dateinamen in den Staging-Verzeichnis haben die Brocken Zahlen eingebettet in ihnen, so die Idee ist, um zu sehen, ob wir für jede Chunk-Nummer eine Datei haben. Die Funktion sollte nur einmal für eine gegebene (vollständige) Datei next() sein.

Allerdings wird meine merge_chunks Funktion mehrmals aufgerufen. (normalerweise zwischen 1 und 4) Logging zeigt, dass es nur aufgerufen nach Ich habe alle Teile erhalten.

In diesem Sinne meine Annahme hier ist, dass es die asynchrone Natur der fs.exists Funktion ist, die das Problem verursacht.

Auch wenn die n ‚th Aufruf von check_completion_status auftreten kann, bevor ich all die Stücke haben, als wir zum n ten Anruf erhalten zu fs.exists(), x mehr Stücke haben gleichzeitig verarbeitet angekommen und wurde, so dass die Funktion kann weitergehen und in einigen Fällen bis zum Ende gehen und next(). Diese Chunks, die gleichzeitig ankommen, werden aber auch den Aufrufen von check_completion_status entsprechen, die auch zu next() gehen, weil wir offensichtlich alle Dateien an diesem Punkt haben.

Das verursacht Probleme, weil ich dies nicht berücksichtigt habe, als ich merge_chunks schrieb.

Der Vollständigkeit halber hier die merge_chunks Funktion:

var merge_chunks = (function() { 

    var pipe_chunks = function (args) { 
    args.chunk_number = args.chunk_number || 1; 
    if (args.chunk_number > args.total_chunks) { 
     args.write_stream.end(); 
     args.next(); 
    } else { 
     var file_name = get_chunk_file_name(args.chunk_number, args.file_id) 
     var read_stream = fs.createReadStream(file_name); 
     read_stream.pipe(args.write_stream, {end: false}); 
     read_stream.on('end', function() { 
     //once we're done with the chunk we can delete it and move on to the next one. 
     fs.unlink(file_name); 
     args.chunk_number += 1; 
     pipe_chunks(args); 
     }); 
    } 
    }; 

    return function (req, res, next) { 
    var out = path.resolve('videos', req.video_id); 
    var write_stream = fs.createWriteStream(out); 
    pipe_chunks({ 
     write_stream: write_stream, 
     file_id: req.file_id, 
     total_chunks: req.total_chunks, 
     next: next 
    }); 
    }; 

}()); 

Derzeit Ich erhalte einen Fehler, da der zweite Aufruf der Funktion versucht, die Stücke zu lesen, die bereits durch den ersten Aufruf gelöscht wurde.

Was ist das typische Muster für die Behandlung dieser Art von Situation? Ich würde gerne eine Stateful-Architektur vermeiden, wenn möglich. Ist es möglich, ausstehende Handler kurz vor dem Aufruf next() in check_completion_status abzubrechen?

+0

'check_completion_status' sieht gut aus mir zeigen. Der einzige Weg, 'merge_chunk' kann mehrmals aufgerufen werden, ist, wenn' next() 'in' check_completion_status' mehrfach ausgelöst wird, und ich sehe nicht, wie das passieren würde. Bist du sicher, dass 'check_completion_status' nicht mehrfach ausgelöst wird? Vermutlich durch vorherige Middleware. –

+0

@YuriZarubin Dies ist die einzige Route, die diese Funktion verwendet. Ich habe ein 'console.log()' am Anfang von 'merge_chunks' gesetzt und es wird nur gefeuert * nachdem * ich alle Chunks erhalten habe (und es gibt keine zwischendurch eingehenden Anfragen). Welche Fehler siehst du in meiner gegenwärtigen Theorie? – LukeP

+0

Ich denke, ich vermisse etwas, was ist Ihre Theorie, warum 'merge_chunk' mehrere Male feuert? Wie ich es sehe, sollte es einmal ausgeführt werden, wenn "current_chunk> req.total_chunks". Ist das nicht der Fall? –

Antwort

1

Wenn Sie nur so schnell wie möglich arbeiten möchten, würde ich eine Sperre (ähnlich einer DB-Sperre) verwenden, um die Ressource zu sperren, so dass nur eine der Anfragen die Chunks verarbeitet. Erstellen Sie einfach eine eindeutige ID auf dem Client und senden Sie sie zusammen mit den Chunks. Speichern Sie dann diese eindeutige ID in einer Art von Datenstruktur und schauen Sie diese ID vor der Verarbeitung an.Das Beispiel ist unten bei weitem nicht optimal (in der Tat wird diese Karte weiter wachsen, was schlecht ist), aber es sollte das Konzept

// Create a map (an array would work too) and keep track of the video ids that were processed. This map will persist through each request. 
var processedVideos = {}; 

var check_completion_status = function (req, res, next) { 
    var current_chunk = 1; 
    var see_if_chunks_exist = function() { 
    fs.exists(get_chunk_file_name(current_chunk, req.file_id), function (exists) { 
     if (processedVideos[req.query.uniqueVideoId]){ 
     res.sendStatus(202); 
     } else if (current_chunk > req.total_chunks) { 
     processedVideos[req.query.uniqueVideoId] = true; 
     next(); 
     } else if (exists) { 
     current_chunk ++; 
     see_if_chunks_exist(); 
     } else { 
     res.sendStatus(202); 
     } 
    }); 
    }; 
    see_if_chunks_exist(); 
}; 
+0

Danke für die Antwort. Ich könnte am Ende eine separate Frage erstellen, die sich darauf konzentriert, den Code besser skalierbar zu machen – LukeP