Saya mencoba untuk mengumpulkan fungsi yang menerima jalur file, mengidentifikasi apa itu, menetapkan header yang sesuai, dan melayani seperti yang dilakukan Apache.
Alasan saya melakukan ini adalah karena saya perlu menggunakan PHP untuk memproses beberapa informasi tentang permintaan sebelum menyajikan file.
Kecepatan sangat penting
virtual () bukanlah pilihan
Harus bekerja di lingkungan hosting bersama di mana pengguna tidak memiliki kendali atas server web (Apache / nginx, dll)
Inilah yang saya dapatkan sejauh ini:
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], '.');
}
}
}
?>
php
performance
file-io
x-sendfile
Kirk Ouimet
sumber
sumber
$extension = end(explode(".", $pathToFile))
, atau Anda dapat melakukannya dengan substr dan strrpos:$extension = substr($pathToFile, strrpos($pathToFile, '.'))
. Selain itu, sebagai penggantimime_content_type()
, Anda dapat mencoba panggilan sistem:$mimetype = exec("file -bi '$pathToFile'", $output);
Jawaban:
Jawaban saya sebelumnya sebagian dan tidak terdokumentasi dengan baik, berikut adalah pembaruan dengan ringkasan solusi darinya dan dari orang lain dalam diskusi.
Solusi tersebut disusun dari solusi terbaik hingga solusi terburuk, tetapi juga dari solusi yang membutuhkan kontrol paling besar atas server web ke solusi yang membutuhkan lebih sedikit. Tampaknya tidak ada cara mudah untuk memiliki satu solusi yang cepat dan berfungsi di mana saja.
Menggunakan header X-SendFile
Seperti yang didokumentasikan oleh orang lain, ini sebenarnya cara terbaik. Dasarnya adalah Anda melakukan kontrol akses di php dan alih-alih mengirim file sendiri, Anda memberi tahu server web untuk melakukannya.
Kode php dasarnya adalah:
Di mana
$file_name
jalur lengkap pada sistem file.Masalah utama dengan solusi ini adalah ia harus diizinkan oleh server web dan tidak diinstal secara default (apache), tidak aktif secara default (lighttpd) atau memerlukan konfigurasi khusus (nginx).
Apache
Di bawah apache jika Anda menggunakan mod_php Anda perlu menginstal modul bernama mod_xsendfile kemudian mengkonfigurasinya (baik di konfigurasi apache atau .htaccess jika Anda mengizinkannya)
Dengan modul ini, jalur file bisa absolut atau relatif terhadap yang ditentukan
XSendFilePath
.Lighttpd
Mod_fastcgi mendukung ini ketika dikonfigurasi dengan
Dokumentasi untuk fitur ini ada di wiki lighttpd, mereka mendokumentasikan
X-LIGHTTPD-send-file
header tetapiX-Sendfile
nama juga berfungsiNginx
Di Nginx Anda tidak dapat menggunakan
X-Sendfile
header Anda harus menggunakan header mereka sendiri yang diberi namaX-Accel-Redirect
. Ini diaktifkan secara default dan satu-satunya perbedaan nyata adalah argumennya harus berupa URI, bukan sistem file. Konsekuensinya adalah Anda harus menentukan lokasi yang ditandai sebagai internal dalam konfigurasi Anda untuk menghindari klien menemukan url file yang sebenarnya dan langsung pergi ke sana, wiki mereka berisi penjelasan yang baik tentang ini.Symlinks dan tajuk Lokasi
Anda dapat menggunakan symlink dan mengarahkan ke mereka, cukup buat symlink ke file Anda dengan nama acak ketika pengguna diberi otorisasi untuk mengakses file dan mengarahkan pengguna ke sana menggunakan:
Jelas Anda memerlukan cara untuk memangkasnya baik ketika skrip untuk membuatnya dipanggil atau melalui cron (di mesin jika Anda memiliki akses atau melalui beberapa layanan webcron)
Di bawah apache, Anda harus dapat mengaktifkannya
FollowSymLinks
di.htaccess
atau di konfigurasi apache.Kontrol akses dengan IP dan header Lokasi
Hack lain adalah menghasilkan file akses apache dari php yang memungkinkan IP pengguna eksplisit. Di bawah apache itu berarti menggunakan
mod_authz_host
(mod_access
)Allow from
perintah.Masalahnya adalah bahwa mengunci akses ke file (karena beberapa pengguna mungkin ingin melakukan ini pada saat yang sama) tidak sepele dan dapat menyebabkan beberapa pengguna menunggu lama. Dan Anda tetap perlu memangkas file tersebut.
Jelas masalah lain adalah bahwa banyak orang di belakang IP yang sama berpotensi mengakses file tersebut.
Ketika semuanya gagal
Jika Anda benar-benar tidak memiliki cara untuk mendapatkan server web Anda untuk membantu Anda, satu-satunya solusi yang tersisa adalah readfile yang tersedia di semua versi php yang saat ini digunakan dan bekerja dengan cukup baik (tetapi tidak terlalu efisien).
Menggabungkan solusi
Baik-baik saja, cara terbaik untuk mengirim file dengan sangat cepat jika Anda ingin kode php Anda dapat digunakan di mana-mana adalah dengan memiliki opsi yang dapat dikonfigurasi di suatu tempat, dengan instruksi tentang cara mengaktifkannya tergantung pada server web dan mungkin deteksi otomatis di instalasi Anda naskah.
Ini sangat mirip dengan apa yang dilakukan di banyak perangkat lunak untuk
mod_rewrite
di apache)mcrypt
modul php)mbstring
modul php)sumber
header("Location: " . $path);
?Cara tercepat: Jangan. Lihat header x-sendfile untuk nginx , ada juga hal serupa untuk server web lain. Ini berarti bahwa Anda masih dapat melakukan kontrol akses dll di php tetapi mendelegasikan pengiriman sebenarnya dari file tersebut ke server web yang dirancang untuk itu.
PS: Saya merasa merinding hanya memikirkan seberapa efisien menggunakan ini dengan nginx, dibandingkan dengan membaca dan mengirim file dalam php. Bayangkan jika 100 orang mengunduh file: Dengan php + apache, menjadi murah hati, itu mungkin 100 * 15mb = 1,5GB (kira-kira, tembak saya), dari ram di sana. Nginx hanya akan mengirimkan file ke kernel, dan kemudian dimuat langsung dari disk ke buffer jaringan. Cepat!
PPS: Dan, dengan metode ini Anda masih dapat melakukan semua kontrol akses, database yang Anda inginkan.
sumber
readfile()
Ini dia solusi PHP murni. Saya telah mengadaptasi fungsi berikut dari kerangka pribadi saya :
Kode ini seefisien mungkin, ia menutup penangan sesi sehingga skrip PHP lain dapat berjalan secara bersamaan untuk pengguna / sesi yang sama. Ini juga mendukung penyajian unduhan dalam rentang (yang juga secara default dilakukan Apache), sehingga orang dapat menjeda / melanjutkan unduhan dan juga mendapatkan keuntungan dari kecepatan unduh yang lebih tinggi dengan akselerator unduhan. Ini juga memungkinkan Anda untuk menentukan kecepatan maksimum (dalam Kbps) di mana unduhan (bagian) harus disajikan melalui
$speed
argumen.sumber
eio
juga tidak selalu tersedia. Tetap saja, +1, tidak tahu tentang ekstensi pecl itu. =)$size = sprintf('%u', filesize($path))
?Biarkan Apache yang bekerja untuk Anda.
sumber
Implementasi yang lebih baik, dengan dukungan cache, header http yang disesuaikan.
sumber
jika Anda memiliki kemungkinan untuk menambahkan ekstensi PECL ke php Anda, Anda cukup menggunakan fungsi dari paket Fileinfo untuk menentukan tipe konten dan kemudian mengirim header yang tepat ...
sumber
Fungsi PHP yang
Download
disebutkan di sini menyebabkan beberapa penundaan sebelum file benar-benar mulai diunduh. Saya tidak tahu apakah ini disebabkan dengan menggunakan pernis cache atau apa, tapi bagi saya itu membantu untuk menghapussleep(1);
sepenuhnya dan set$speed
ke1024
. Sekarang bekerja tanpa masalah secepat neraka. Mungkin Anda bisa memodifikasi fungsi itu juga, karena saya melihatnya digunakan di seluruh internet.sumber
Saya membuat kode fungsi yang sangat sederhana untuk melayani file dengan PHP dan deteksi tipe MIME otomatis:
Pemakaian
sumber