Rumah  >  Artikel  >  pembangunan bahagian belakang  >  Bagaimana untuk Membaca dan Gema Menulis Saiz Fail yang Dimuat Naik Pada Masa Sedang Dalam Pelayan dalam Masa Nyata Tanpa Menyekat Pelayan dan Pelanggan?

Bagaimana untuk Membaca dan Gema Menulis Saiz Fail yang Dimuat Naik Pada Masa Sedang Dalam Pelayan dalam Masa Nyata Tanpa Menyekat Pelayan dan Pelanggan?

DDD
DDDasal
2024-10-20 22:02:30758semak imbas

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

Bagaimana untuk membaca dan mencetak saiz fail yang dimuat naik yang ditulis pada bahagian pelayan dalam masa nyata tanpa menyekat pelayan dan klien?

Mari kita meluaskan isu ini:

Untuk mendapatkan kemajuan muat naik fail dalam masa nyata, kami menetapkannya daripada objek Blob, File, TypedArray atau ArrayBuffer melalui pengambilan () dalam permintaan POST Objek badan.

Pelaksanaan semasa menetapkan objek Fail sebagai hujah kedua untuk mengambil() dihantar ke objek badan.

Keperluan:

Sebagai teks/strim-acara membaca saiz fail yang ditulis ke sistem fail pelayan dan menggemakannya kembali kepada klien. Berhenti apabila semua bait yang disediakan oleh parameter rentetan pertanyaan var dalam permintaan GET telah ditulis. Pembacaan fail pada masa ini berlaku dalam persekitaran skrip yang berasingan, di mana panggilan GET dibuat ke skrip yang membaca fail, dan kemudian POST dibuat pada skrip yang menulis fail ke pelayan.

Tunggu sehingga anda melengkapkan bahagian bergema saiz fail sebelum cuba menyelesaikan isu yang mungkin berlaku dengan mengendalikan penulisan fail sebelah pelayan atau bacaan fail untuk mendapatkan saiz fail semasa.

Pada masa ini cuba menggunakan php untuk memenuhi keperluan. Tetapi juga berminat dengan c, bash, nodejs, python atau bahasa lain atau kaedah yang boleh digunakan untuk melaksanakan tugas yang sama.

Bahagian javascript sisi klien baik. Saya hanya tidak mahir dalam php (salah satu bahasa bahagian pelayan yang paling banyak digunakan di dunia) untuk melaksanakan corak tanpa memasukkan bahagian yang tidak perlu.

Motivasi:

Penunjuk kemajuan untuk diambil?

Berkaitan:

Ambil dengan ReadableStream

Soalan:

Dapatkan

PHP Notice:  Undefined index: HTTP_LAST_EVENT_ID in stream.php on line 7

di terminal.

Selain itu, jika anda menggantikan

while(file_exists($_GET["filename"]) 
  &amp;&amp; filesize($_GET["filename"]) < intval($_GET["filesize"]))

dengan

while(true)

ia akan menjana ralat di EventSource.

Tanpa panggilan sleep(), saiz fail yang betul dihantar ke acara mesej untuk fail bersaiz 3.3MB, 3321824, 61921, 26214 dan 38093 masing-masing dicetak apabila fail yang sama dimuat naik tiga kali. Hasil yang dijangkakan ialah untuk mendapatkan saiz fail semasa menulis fail di:

stream_copy_to_stream($input, $file);

dan bukannya saiz fail objek fail yang dimuat naik. Adakah fopen() atau stream_copy_to_stream() akan menghalang proses php lain daripada mengakses stream.php?

Perkara yang dicuba setakat ini:

Petikan php daripada

  • Melebihi $_POST, $_GET dan $_FILE: Dalam JavaScriptPHP Handling Blobs
  • Pengenalan kepada Acara Dihantar Pelayan dengan Contoh PHP

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>

Atas ialah kandungan terperinci Bagaimana untuk Membaca dan Gema Menulis Saiz Fail yang Dimuat Naik Pada Masa Sedang Dalam Pelayan dalam Masa Nyata Tanpa Menyekat Pelayan dan Pelanggan?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn