Wie kann ich die Größe einer hochgeladenen Datei, die auf der Serverseite geschrieben wird, in Echtzeit lesen und drucken, ohne den Server und den Client zu blockieren?
Lassen Sie uns dieses Problem näher erläutern:
Um den Fortschritt des Datei-Uploads in Echtzeit zu erhalten, legen wir ihn durch Abrufen aus dem Blob-, File-, TypedArray- oder ArrayBuffer-Objekt fest () in der POST-Anfrage Das Body-Objekt.
Die aktuelle Implementierung legt das File-Objekt als zweites Argument für fetch() fest, das an das Body-Objekt übergeben wird.
Anforderungen:
Als Text-/Ereignisstrom liest er die Größe einer Datei, die in das Dateisystem des Servers geschrieben wird, und gibt sie an den Client zurück. Stoppt, wenn alle vom var-Abfragezeichenfolgenparameter in der GET-Anfrage bereitgestellten Bytes geschrieben wurden. Das Lesen von Dateien erfolgt derzeit in einer separaten Skriptumgebung, in der GET-Aufrufe an das Skript erfolgen, das die Datei liest, und anschließend POST an das Skript, das die Datei auf den Server schreibt.
Warten Sie, bis Sie den Teil zum Echo der Dateigröße abgeschlossen haben, bevor Sie versuchen, potenzielle Probleme bei der Verarbeitung serverseitiger Dateischreibvorgänge oder Dateilesevorgänge zu beheben, um die aktuelle Dateigröße zu ermitteln.
Derzeit versuche ich, PHP zu verwenden, um die Anforderungen zu erfüllen. Aber auch an C, Bash, NodeJS, Python oder anderen Sprachen oder Methoden interessiert, die zur Ausführung derselben Aufgabe verwendet werden können.
Der clientseitige Javascript-Teil ist in Ordnung. Ich kenne mich mit PHP (einer der am weitesten verbreiteten serverseitigen Sprachen der Welt) einfach nicht gut aus, um das Muster zu implementieren, ohne unnötige Teile einzuschließen.
Motivation:
Fortschrittsanzeige für Abruf?
Verwandte Themen:
Abrufen mit ReadableStream
Frage:
Get
PHP Notice: Undefined index: HTTP_LAST_EVENT_ID in stream.php on line 7
im Terminal.
Außerdem wird, wenn Sie
while(file_exists($_GET["filename"]) && filesize($_GET["filename"]) < intval($_GET["filesize"]))
durch
while(true)
ersetzen, ein Fehler an der EventSource generiert.
Ohne den Aufruf von „sleep()“ wird die richtige Dateigröße an das Nachrichtenereignis für eine Datei mit einer Größe von 3,3 MB gesendet. 3321824, 61921, 26214 und 38093 werden jeweils gedruckt, wenn dieselbe Datei dreimal hochgeladen wird. Das erwartete Ergebnis besteht darin, die Dateigröße beim Schreiben einer Datei unter
stream_copy_to_stream($input, $file);
anstelle der Dateigröße des hochgeladenen Dateiobjekts zu erhalten. Verhindern fopen() oder stream_copy_to_stream(), dass andere PHP-Prozesse auf stream.php zugreifen?
Bisher ausprobierte Dinge:
php Zitat aus
PHP
// 能否合并 `data.php`、`stream.php` 为同一个文件? // 能否使用 `STREAM_NOTIFY_PROGRESS` // "Indicates current progress of the stream transfer // in bytes_transferred and possibly bytes_max as well" to read bytes? // do we need to call `stream_set_blocking` to `false` // data.php <?php $filename = $_SERVER["HTTP_X_FILENAME"]; $input = fopen("php://input", "rb"); $file = fopen($filename, "wb"); stream_copy_to_stream($input, $file); fclose($input); fclose($file); echo "upload of " . $filename . " successful"; ?>
// stream.php <?php header("Content-Type: text/event-stream"); header("Cache-Control: no-cache"); header("Connection: keep-alive"); // `PHP Notice: Undefined index: HTTP_LAST_EVENT_ID in stream.php on line 7` ? $lastId = $_SERVER["HTTP_LAST_EVENT_ID"] || 0; if (isset($lastId) && !empty($lastId) && is_numeric($lastId)) { $lastId = intval($lastId); $lastId++; } // else { // $lastId = 0; // } // while current file size read is less than or equal to // `$_GET["filesize"]` of `$_GET["filename"]` // how to loop only when above is `true` while (true) { $upload = $_GET["filename"]; // is this the correct function and variable to use // to get written bytes of `stream_copy_to_stream($input, $file);`? $data = filesize($upload); // $data = $_GET["filename"] . " " . $_GET["filesize"]; if ($data) { sendMessage($lastId, $data); $lastId++; } // else { // close stream // } // not necessary here, though without thousands of `message` events // will be dispatched // sleep(1); } function sendMessage($id, $data) { echo "id: $id\n"; echo "data: $data\n\n"; ob_flush(); flush(); } ?>
Javascript
<!DOCTYPE html> <html> <head> </head> <body> <input type="file"> <progress value="0" max="0" step="1"></progress> <script> const [url, stream, header] = ["data.php", "stream.php", "x-filename"]; const [input, progress, handleFile] = [ document.querySelector("input[type=file]") , document.querySelector("progress") , (event) => { const [file] = input.files; const [{size:filesize, name:filename}, headers, params] = [ file, new Headers(), new URLSearchParams() ]; // set `filename`, `filesize` as search parameters for `stream` URL Object.entries({filename, filesize}) .forEach(([...props]) => params.append.apply(params, props)); // set header for `POST` headers.append(header, filename); // reset `progress.value` set `progress.max` to `filesize` [progress.value, progress.max] = [0, filesize]; const [request, source] = [ new Request(url, { method:"POST", headers:headers, body:file }) // https://stackoverflow.com/a/42330433/ , new EventSource(`${stream}?${params.toString()}`) ]; source.addEventListener("message", (e) => { // update `progress` here, // call `.close()` when `e.data === filesize` // `progress.value = e.data`, should be this simple console.log(e.data, e.lastEventId); }, true); source.addEventListener("open", (e) => { console.log("fetch upload progress open"); }, true); source.addEventListener("error", (e) => { console.error("fetch upload progress error"); }, true); // sanity check for tests, // we don't need `source` when `e.data === filesize`; // we could call `.close()` within `message` event handler setTimeout(() => source.close(), 30000); // we don't need `source' to be in `Promise` chain, // though we could resolve if `e.data === filesize` // before `response`, then wait for `.text()`; etc. // TODO: if and where to merge or branch `EventSource`, // `fetch` to single or two `Promise` chains const upload = fetch(request); upload .then(response => response.text()) .then(res => console.log(res)) .catch(err => console.error(err)); } ]; input.addEventListener("change", handleFile, true); </script> </body> </html>
Das obige ist der detaillierte Inhalt vonWie kann ich die aktuelle Schreibgröße der hochgeladenen Datei auf dem Server in Echtzeit lesen und wiedergeben, ohne Server und Client zu blockieren?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!