programing

PHP를 사용하여 파일을 가장 빠르게 처리하는 방법

copyandpastes 2022. 9. 25. 22:27
반응형

PHP를 사용하여 파일을 가장 빠르게 처리하는 방법

Apache처럼 파일 경로를 수신하고, 파일 경로를 식별하고, 적절한 헤더를 설정하고, 서비스를 제공하는 함수를 만들려고 합니다.

이 작업을 하는 이유는 파일을 제공하기 전에 PHP를 사용하여 요청에 대한 정보를 처리해야 하기 때문입니다.

속도가 중요

virtual()은 옵션이 아닙니다.

사용자가 웹 서버를 제어할 수 없는 공유 호스팅 환경에서 작업해야 합니다(Apache/nginx 등).

지금까지 알아낸 내용은 다음과 같습니다.

File::output($path);

<?php
class File {
static function output($path) {
    // Check if the file exists
    if(!File::exists($path)) {
        header('HTTP/1.0 404 Not Found');
        exit();
    }

    // Set the content-type header
    header('Content-Type: '.File::mimeType($path));

    // Handle caching
    $fileModificationTime = gmdate('D, d M Y H:i:s', File::modificationTime($path)).' GMT';
    $headers = getallheaders();
    if(isset($headers['If-Modified-Since']) && $headers['If-Modified-Since'] == $fileModificationTime) {
        header('HTTP/1.1 304 Not Modified');
        exit();
    }
    header('Last-Modified: '.$fileModificationTime);

    // Read the file
    readfile($path);

    exit();
}

static function mimeType($path) {
    preg_match("|\.([a-z0-9]{2,4})$|i", $path, $fileSuffix);

    switch(strtolower($fileSuffix[1])) {
        case 'js' :
            return 'application/x-javascript';
        case 'json' :
            return 'application/json';
        case 'jpg' :
        case 'jpeg' :
        case 'jpe' :
            return 'image/jpg';
        case 'png' :
        case 'gif' :
        case 'bmp' :
        case 'tiff' :
            return 'image/'.strtolower($fileSuffix[1]);
        case 'css' :
            return 'text/css';
        case 'xml' :
            return 'application/xml';
        case 'doc' :
        case 'docx' :
            return 'application/msword';
        case 'xls' :
        case 'xlt' :
        case 'xlm' :
        case 'xld' :
        case 'xla' :
        case 'xlc' :
        case 'xlw' :
        case 'xll' :
            return 'application/vnd.ms-excel';
        case 'ppt' :
        case 'pps' :
            return 'application/vnd.ms-powerpoint';
        case 'rtf' :
            return 'application/rtf';
        case 'pdf' :
            return 'application/pdf';
        case 'html' :
        case 'htm' :
        case 'php' :
            return 'text/html';
        case 'txt' :
            return 'text/plain';
        case 'mpeg' :
        case 'mpg' :
        case 'mpe' :
            return 'video/mpeg';
        case 'mp3' :
            return 'audio/mpeg3';
        case 'wav' :
            return 'audio/wav';
        case 'aiff' :
        case 'aif' :
            return 'audio/aiff';
        case 'avi' :
            return 'video/msvideo';
        case 'wmv' :
            return 'video/x-ms-wmv';
        case 'mov' :
            return 'video/quicktime';
        case 'zip' :
            return 'application/zip';
        case 'tar' :
            return 'application/x-tar';
        case 'swf' :
            return 'application/x-shockwave-flash';
        default :
            if(function_exists('mime_content_type')) {
                $fileSuffix = mime_content_type($path);
            }
            return 'unknown/' . trim($fileSuffix[0], '.');
    }
}
}
?>

이전 답변은 부분적이고 문서화되어 있지 않습니다.다음은 그 답변과 논의 중인 다른 답변의 해결책을 정리한 업데이트입니다.

솔루션은 최적의 솔루션부터 최악의 솔루션까지 다양하지만 웹 서버를 가장 잘 제어할 필요가 있는 솔루션부터 덜 필요한 솔루션까지 다양합니다.빠르고 어디서나 작동하는 하나의 솔루션을 쉽게 구할 수 있는 방법은 없는 것 같습니다.


X-SendFile 헤더 사용

다른 사람들이 증명한 바와 같이, 이것이 실제로 가장 좋은 방법입니다.기본은 액세스 제어를 php로 하고 파일을 직접 보내는 대신 웹 서버에 지시하는 것입니다.

기본 php 코드는 다음과 같습니다.

header("X-Sendfile: $file_name");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($file_name) . '"');

서 ★★★★★$file_name는파파파 시풀풀풀풀풀

이 솔루션의 주요 문제는 웹 서버에서 허용해야 하며 기본적으로 설치되지 않거나(apache), 기본적으로 활성화되지 않거나(lighttpd), 특정 구성이 필요하다는 것입니다(nginx).

아파치

mod_php를 사용하는 경우 apache에서 mod_xsendfile이라는 모듈을 설치한 후 구성해야 합니다(apache config 또는 .htaccess(허용하는 경우).

XSendFile on
XSendFilePath /home/www/example.com/htdocs/files/

된 파일 경로를 로 지정할 수 .XSendFilePath.

라이트TPD

mod_fastcgi는 이 기능을 지원합니다.

"allow-x-send-file" => "enable" 

이 기능의 매뉴얼은 lighttpd wiki에 기재되어 있습니다.X-LIGHTTPD-send-file단, 「」의X-Sendfile도 워크네임입니다.

엔긴스

에서는 Nginx를 수 .X-Sendfile는 header라는 .X-Accel-Redirect. 디폴트로 유효하게 되어 있습니다.또, 실제의 차이는, 인수가 파일시스템이 아닌 URI여야 한다는 점입니다.따라서 클라이언트가 실제 파일 URL을 검색하여 직접 해당 URL로 이동하는 것을 방지하기 위해 설정에서 내부로 마크된 위치를 정의해야 합니다.wiki에는 이에 대한 설명이 포함되어 있습니다.

Symlinks and Location 헤더

사용자가 파일에 액세스할 권한이 있을 때 무작위로 파일에 대한 심볼 링크를 생성하고 다음 방법을 사용하여 해당 파일로 리디렉션하면 symlinks를 사용하여 리디렉션할 수 있습니다.

header("Location: " . $url_of_symlink);

분명히 작성하기 위한 스크립트가 호출되었을 때 또는 cron 경유(액세스 권한이 있는 경우 머신 상에서, 그렇지 않은 경우 webcron 서비스를 통해)를 프루닝할 수 있는 방법이 필요합니다.

Apache에서 또는 Apache 구성에서 활성화할 수 있어야 합니다.

IP 및 Location 헤더에 의한 접근 제어

또 다른 해킹은 명시적인 사용자 IP를 허용하는 php에서 아파치 액세스 파일을 생성하는 것입니다.apache에서는 (를 사용하는 것을 의미합니다.mod_accessAllow from명령어를 지정합니다.

문제는 (여러 사용자가 동시에 이 작업을 수행할 수 있으므로) 파일에 대한 액세스를 잠그는 것은 사소한 일이 아니며 일부 사용자가 장시간 대기할 수 있다는 것입니다.그리고 당신은 여전히 파일을 잘라내야 해

또 다른 문제는 같은 IP의 배후에 있는 복수의 사용자가 파일에 액세스 할 가능성이 있다는 것입니다.

다른 모든 것이 실패했을 때

웹 서버를 사용할 방법이 없다면 현재 사용 중인 모든 php 버전에서 사용할 수 있는 readfile이 유일한 솔루션입니다(그러나 실제로는 효율적이지 않습니다).


솔루션의 조합

php 코드를 어디에서나 사용할 수 있도록 하려면 파일을 매우 빨리 보내는 가장 좋은 방법은 웹 서버에 따라 활성화하는 방법과 설치 스크립트에서 자동 검출하는 방법을 포함한 구성 가능한 옵션을 어딘가에 두는 것입니다.

많은 소프트웨어에서 실행되는 것과 거의 비슷합니다.

  • url(url)mod_rewriteapache)의 경우)
  • 함수 「」 「」( 「」)mcrypt module을 클릭합니다.
  • 지원mbstring module을 클릭합니다.

가장 빠른 방법:하지 마세요. nginx의 x-sendfile 헤더를 보세요. 다른 웹 서버에도 비슷한 것이 있습니다.즉, 액세스 제어 등은 php로 할 수 있지만 실제 파일 송신은 이를 위해 설계된 웹 서버에 위임할 수 있습니다.

p.s. 파일을 읽고 php로 보내는 것에 비해 nginx에서 사용하는 것이 얼마나 효율적일지 생각만 해도 소름이 끼칩니다.100명의 사용자가 파일을 다운로드하고 있다고 가정해 보십시오.php + apache의 경우 100*15mb = 1.5일 것입니다.GB(약, 쏴주세요)의 램이 바로 여기에 있습니다.Nginx는 파일을 커널로 보내는 것을 핸드오프하고 디스크에서 네트워크 버퍼로 직접 로드합니다.빠르게!

P.P.S. 이 방법으로는 접근통제나 데이터베이스 등의 작업을 모두 수행할 수 있습니다.

여기 순수한 PHP 솔루션이 있습니다.개인 프레임워크에서 다음과 같은 기능을 채택했습니다.

function Download($path, $speed = null, $multipart = true)
{
    while (ob_get_level() > 0)
    {
        ob_end_clean();
    }

    if (is_file($path = realpath($path)) === true)
    {
        $file = @fopen($path, 'rb');
        $size = sprintf('%u', filesize($path));
        $speed = (empty($speed) === true) ? 1024 : floatval($speed);

        if (is_resource($file) === true)
        {
            set_time_limit(0);

            if (strlen(session_id()) > 0)
            {
                session_write_close();
            }

            if ($multipart === true)
            {
                $range = array(0, $size - 1);

                if (array_key_exists('HTTP_RANGE', $_SERVER) === true)
                {
                    $range = array_map('intval', explode('-', preg_replace('~.*=([^,]*).*~', '$1', $_SERVER['HTTP_RANGE'])));

                    if (empty($range[1]) === true)
                    {
                        $range[1] = $size - 1;
                    }

                    foreach ($range as $key => $value)
                    {
                        $range[$key] = max(0, min($value, $size - 1));
                    }

                    if (($range[0] > 0) || ($range[1] < ($size - 1)))
                    {
                        header(sprintf('%s %03u %s', 'HTTP/1.1', 206, 'Partial Content'), true, 206);
                    }
                }

                header('Accept-Ranges: bytes');
                header('Content-Range: bytes ' . sprintf('%u-%u/%u', $range[0], $range[1], $size));
            }

            else
            {
                $range = array(0, $size - 1);
            }

            header('Pragma: public');
            header('Cache-Control: public, no-cache');
            header('Content-Type: application/octet-stream');
            header('Content-Length: ' . sprintf('%u', $range[1] - $range[0] + 1));
            header('Content-Disposition: attachment; filename="' . basename($path) . '"');
            header('Content-Transfer-Encoding: binary');

            if ($range[0] > 0)
            {
                fseek($file, $range[0]);
            }

            while ((feof($file) !== true) && (connection_status() === CONNECTION_NORMAL))
            {
                echo fread($file, round($speed * 1024)); flush(); sleep(1);
            }

            fclose($file);
        }

        exit();
    }

    else
    {
        header(sprintf('%s %03u %s', 'HTTP/1.1', 404, 'Not Found'), true, 404);
    }

    return false;
}

이 코드는 최대한 효율적이며 세션핸들러를 닫고 다른 PHP 스크립트를 동일한 사용자/세션에서 동시에 실행할 수 있도록 합니다.또한 다운로드 범위(Apache가 기본적으로 수행하는 작업이기도 함)에서의 다운로드 서비스도 지원하므로 다운로드 액셀러레이터를 통해 다운로드를 일시 중지/재개할 수 있고 다운로드 속도가 빨라지는 이점을 누릴 수 있습니다.또, 다운로드(부품)의 처리 속도를 Kbps 단위로 지정할 수도 있습니다.$speed논쟁.

header('Location: ' . $path);
exit(0);

Apache가 대신 일하게 해 주세요.

커스터마이즈된 http 헤더를 캐시 지원으로 구현하기 쉬워졌습니다.

serveStaticFile($fn, array(
        'headers'=>array(
            'Content-Type' => 'image/x-icon',
            'Cache-Control' =>  'public, max-age=604800',
            'Expires' => gmdate("D, d M Y H:i:s", time() + 30 * 86400) . " GMT",
        )
    ));

function serveStaticFile($path, $options = array()) {
    $path = realpath($path);
    if (is_file($path)) {
        if(session_id())
            session_write_close();

        header_remove();
        set_time_limit(0);
        $size = filesize($path);
        $lastModifiedTime = filemtime($path);
        $fp = @fopen($path, 'rb');
        $range = array(0, $size - 1);

        header('Last-Modified: ' . gmdate("D, d M Y H:i:s", $lastModifiedTime)." GMT");
        if (( ! empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == $lastModifiedTime ) ) {
            header("HTTP/1.1 304 Not Modified", true, 304);
            return true;
        }

        if (isset($_SERVER['HTTP_RANGE'])) {
            //$valid = preg_match('^bytes=\d*-\d*(,\d*-\d*)*$', $_SERVER['HTTP_RANGE']);
            if(substr($_SERVER['HTTP_RANGE'], 0, 6) != 'bytes=') {
                header('HTTP/1.1 416 Requested Range Not Satisfiable', true, 416);
                header('Content-Range: bytes */' . $size); // Required in 416.
                return false;
            }

            $ranges = explode(',', substr($_SERVER['HTTP_RANGE'], 6));
            $range = explode('-', $ranges[0]); // to do: only support the first range now.

            if ($range[0] === '') $range[0] = 0;
            if ($range[1] === '') $range[1] = $size - 1;

            if (($range[0] >= 0) && ($range[1] <= $size - 1) && ($range[0] <= $range[1])) {
                header('HTTP/1.1 206 Partial Content', true, 206);
                header('Content-Range: bytes ' . sprintf('%u-%u/%u', $range[0], $range[1], $size));
            }
            else {
                header('HTTP/1.1 416 Requested Range Not Satisfiable', true, 416);
                header('Content-Range: bytes */' . $size);
                return false;
            }
        }

        $contentLength = $range[1] - $range[0] + 1;

        //header('Content-Disposition: attachment; filename="xxxxx"');
        $headers = array(
            'Accept-Ranges' => 'bytes',
            'Content-Length' => $contentLength,
            'Content-Type' => 'application/octet-stream',
        );

        if(!empty($options['headers'])) {
            $headers = array_merge($headers, $options['headers']);
        }
        foreach($headers as $k=>$v) {
            header("$k: $v", true);
        }

        if ($range[0] > 0) {
            fseek($fp, $range[0]);
        }
        $sentSize = 0;
        while (!feof($fp) && (connection_status() === CONNECTION_NORMAL)) {
            $readingSize = $contentLength - $sentSize;
            $readingSize = min($readingSize, 512 * 1024);
            if($readingSize <= 0) break;

            $data = fread($fp, $readingSize);
            if(!$data) break;
            $sentSize += strlen($data);
            echo $data;
            flush();
        }

        fclose($fp);
        return true;
    }
    else {
        header('HTTP/1.1 404 Not Found', true, 404);
        return false;
    }
}

PECL 확장자를 php에 추가할 수 있는 경우 Fileinfo 패키지의 함수를 사용하여 콘텐츠 유형을 결정하고 적절한 헤더를 전송할 수 있습니다.

PHPDownload여기서 언급한 함수가 파일의 실제 다운로드를 시작하기 전에 약간의 지연을 초래했습니다.이게 니시 캐쉬를 사용해서 그런지는 모르겠지만, 나한테는 그게 더 큰 문제를 해결하는데sleep(1);완전무결하게$speed로.1024. 지금은 매우 빠르게 문제없이 작동합니다.아마 당신도 그 기능을 수정할 수 있을 거예요.인터넷에서 다 봤어요.

PHP와 자동 MIME 타입 검출로 파일을 처리하는 매우 간단한 함수를 코드화했습니다.

function serve_file($filepath, $new_filename=null) {
    $filename = basename($filepath);
    if (!$new_filename) {
        $new_filename = $filename;
    }
    $mime_type = mime_content_type($filepath);
    header('Content-type: '.$mime_type);
    header('Content-Disposition: attachment; filename="downloaded.pdf"');
    readfile($filepath);
}

사용.

serve_file("/no_apache/invoice243.pdf");

언급URL : https://stackoverflow.com/questions/3697748/fastest-way-to-serve-a-file-using-php

반응형