Home >Backend Development >PHP Tutorial >PHP asynchronous coroutine development: solving the problem of uploading and downloading large files

PHP asynchronous coroutine development: solving the problem of uploading and downloading large files

PHPz
PHPzOriginal
2023-12-18 17:09:31977browse

PHP asynchronous coroutine development: solving the problem of uploading and downloading large files

With the development of network technology and the continuous expansion of application scenarios, uploading and downloading large files has become a problem faced by many web applications. Traditional processing methods are often time-consuming and inefficient, while PHP asynchronous coroutine development can effectively solve these problems.

In recent years, the asynchronous programming technology of PHP language has gradually been widely used, among which coroutine technology has been more widely used in actual development. A coroutine is an advanced form of user threading that allows a thread to interrupt, wait for some event to occur, and then resume execution of the thread. In layman's terms, it means actively giving up the CPU to perform some other operations during code execution.

The following will introduce in detail the application of PHP asynchronous coroutine development in large file upload and download.

1. Large file upload

In Web applications, large file uploads are generally implemented through the HTTP protocol. When a user uploads a large file, the server needs to read the file into memory and write it to disk. This process takes a lot of time and resources. If in the traditional processing method, once a large file is uploaded, the server will wait for the upload to complete and cannot handle other requests at the same time. This not only wastes resources but also affects user experience.

Solution based on coroutine:

1. The client uploads the file in slices to the server. Here, H5’s FormData API and XMLHttpRequest object are used to implement it

2. Server After receiving the upload request, check whether the number of slices of the uploaded file is consistent with the file size. If they are consistent, the received slices are stored in the target file.

3. If they are inconsistent, an error message will be returned. If any file block fails to be received, other uploaded blocks should be cleaned up to avoid producing semi-finished files.

4. After the upload is completed, the server can operate the file attributes, etc. If the file is relatively large, the file can be processed asynchronously to avoid IO and CPU intensive sensitivity to the CPU.

The following is a sample code:

<?php
// 启用协程运行时
SwooleRuntime::enableCoroutine();

$http = new SwooleHttpServer("127.0.0.1", 9501);

// 监听HTTP请求
$http->on("request", function ($request, $response) {

    // 从请求中获取分块数据
    $chunk = $request->rawContent();

    // 获取分块所属的文件名和分块编号
    $fileName = $_POST['fileName'];
    $chunkIndex = $_POST['chunkIndex'];

    // 将分块数据追加写入到目标文件中
    $fp = fopen($fileName, 'ab');
    fwrite($fp, $chunk);
    fclose($fp);

    // 判断是否上传完成
    if (intval($_POST['totalChunks']) == $chunkIndex + 1) {
        $response->end("Upload completed.");
    } else {
        $response->end("Upload success.");
    }
});

$http->start();

2. Large file download

In Web applications, large file downloads are also implemented through the HTTP protocol. When a user needs to download a large file, the server needs to read the file from the disk and send it to the client. This process also takes a lot of time and resources. If in the traditional processing method, the server reads the entire file into memory at one time and sends it to the client, this not only wastes resources, but may also cause the server to crash.

Solution based on coroutine:

1. Read a certain block of data from the disk each time and send it to the client

2. Use coroutine for control , yield yields up the CPU after sending a certain amount of data. 3. After the client has consumed the current block, it sends a message to the server and enters the next block of data.

The following It is a sample code:

<?php
// 启用协程运行时
SwooleRuntime::enableCoroutine();

$server = new SwooleHttpServer('127.0.0.1', 9502);

$server->on('request', function($request, $response) {
    $filePath = '/path/to/large/file';
    $startPos = 0;
    $readChunkSize = 8192;

    $fileSize = filesize($filePath);

    $response->header('Content-Type', 'application/octet-stream');
    $response->header('Accept-Ranges', 'bytes');

    // 读取和发送一块数据
    function readAndSendChunk($fp, $response, $startPos, $readChunkSize, $fileSize) {
        fseek($fp, $startPos);
        $maxLength = $fileSize - $startPos;
        if ($maxLength > $readChunkSize) {
            $maxLength = $readChunkSize;
        }
        $data = fread($fp, $maxLength);
        $response->write($data);
        return $startPos + $maxLength;
    }

    // 每发送一定量的数据后yield,让出CPU
    function sendByYield($fp, $response, $startPos, $readChunkSize, $fileSize) {
        while ($startPos < $fileSize) {
            $startPos = readAndSendChunk($fp, $response, $startPos, $readChunkSize, $fileSize);
            yield;
        }
        fclose($fp);
        $response->end();
    }

    // 检查是否支持断点续传
    $range = $request->header['range'];
    if ($range) {
        $status = '206 Partial Content';
        $range = explode('-', substr($range, 6));
        if ($range[0] === '') {
            $startPos = $fileSize - intval($range[1]);
        } else if ($range[1] === '') {
            $startPos = intval($range[0]);
        } else {
            $startPos = intval($range[0]);
            $readChunkSize = intval($range[1]) - $startPos + 1;
            $response->header('Content-Length', $readChunkSize);
        }
    } else {
        $status = '200 OK';
        $response->header('Content-Length', $fileSize);
    }

    $response->header('HTTP/1.1', $status);
    $response->header('Content-Disposition', 'attachment;filename="'.basename($filePath).'"');
    $response->header('Content-Range', 'bytes '.$startPos.'-'.($startPos+$readChunkSize-1).'/'.$fileSize);

    $fp = fopen($filePath, 'rb');
    fseek($fp, $startPos);

    $response->status(200);

    // 使用协程进行控制
    for ($i = 1; $i <= 5; $i++) {
        go(function() use ($fp, $response, $startPos, $readChunkSize, $fileSize) {
            yield from sendByYield($fp, $response, $startPos, $readChunkSize, $fileSize);
        });
    }
});

$server->start();

Conclusion:

This article introduces in detail the application of PHP asynchronous coroutine development in large file uploading and downloading, and gives specific code implementation examples. In actual development, the use of coroutine-based asynchronous programming technology can effectively improve the processing performance and user experience of web applications, which is worthy of in-depth research and exploration by developers.

The above is the detailed content of PHP asynchronous coroutine development: solving the problem of uploading and downloading large files. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn