Heim  >  Artikel  >  Backend-Entwicklung  >  Wie kann ich die aktuelle Schreibgröße der hochgeladenen Datei auf dem Server in Echtzeit lesen und wiedergeben, ohne Server und Client zu blockieren?

Wie kann ich die aktuelle Schreibgröße der hochgeladenen Datei auf dem Server in Echtzeit lesen und wiedergeben, ohne Server und Client zu blockieren?

DDD
DDDOriginal
2024-10-20 22:02:30758Durchsuche

How to Read and Echo Currently Writing Size of Uploaded File in Server in Realtime Without Blocking Server and Client?

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"]) 
  &amp;&amp; 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

  • Beyond $_POST, $_GET and $_FILE: In JavaScriptPHP Handling Blobs
  • Einführung in vom Server gesendete Ereignisse mit PHP-Beispielen

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) &amp;&amp; !empty($lastId) &amp;&amp; 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!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn