Php content type application octet stream

Советы по PHP : Отправка писем при помощи PHP : Прикрепление файла

Письмо, содержащее прикрепленный файл, несколько отличается от простого. В нем добавляются некоторые заголовки, и немного меняется само построение, хотя несомненно само письмо остается текстовым файлом. Но давайте все по порядку.

Одна из особенностей — наличие заголовка Mime-Version.

Этот заголовок указывает стандарт, которому соответствует тело сообщения.

Сообщения, соответствующие MIME обязаны содержать такое поле заголовка со следующим текстом:

Если мы хотим отослать письмо с прикрепленными файлами, то необходимо использовать заголовок

Content-type: multipart/mixed

который обозначает, что письмо состоит из нескольких частей, каждая из которых содержит свой заголовок Content-type.

Для обозначения границы этих частей необходимо использовать параметр boundary, который еще называется маркером границы.

Значением этого параметра может служить любая строка. Но надо учесть, что она должна быть уникальной и не встречалась в теле письма. Иначе письмо может быть неправильно разбито на части.

From: "Uspenskii Evgeny" [email protected]> To: [email protected] Subject: Hello Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="spravkaweb-12345"

При разделении письма на части перед маркером должны стоять два знака дефиса.

А последний маркер, который обозначает конец письма, должен содержать в конце два знака дефиса.

Для каждой части необходимо установить свои заголовки.

После заголовков необходимо поставить два знака перевода строки.

From: "Uspenskii Evgeny" [email protected]> To: [email protected] Subject: Hello Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="spravkaweb-12345" --spravkaweb-1234 Content-type: text/plain; charset="windows-1251" Content-Transfer-Encoding: quoted-printable Hi! Here's that my file! --spravkaweb-1234 Content-Type: application/x-rar-compressed; name="file.rar" Content-Transfer-Encoding:base64 Content-Disposition:attachment UmFyIRoHAM+QcwAADQAAAAAAAABvYXQg . spravkaweb-1234--

Если у нас идет часть с текстом, то надо заголовок Content-Transfer-Encoding присвоить значение quoted-printable, либо 7bit, либо 8bit.

Для части с файлом этот заголовок должен быть равен base64.

Заголовок Content-Disposition, присутствующий во второй части, указывает, как почтовой программе следует отобразить данную часть письма. Он может принимать значение attachment (этот участок не является частью письма, а просто прикреплен к нему в виде файла) и inline (включение, которое используется непосредственно в письме, например, картинка, вставляемая в HTML).

В первой части заголовком

Content-type: text/plain; charset="windows-1251"

указали, что это простой текст с кодировкой Windows.

Во второй же части заголовком

Content-Type: application/x-rar-compressed; name="file.rar"

указали, что тип файла — rar-архив, а имя файла — file.rar.

Если отправляем gif-картинку, его Content-type будет выглядеть:

Content-Type: image/gif; name="file.gif"

В случае, если заранее не известно, какой тип файла отправляем, или формат файла не является стандартным, следует заголовку Content-type присвоить значение application/octet-stream.

Content-Type: application/octet-stream; name="file.dat"

Прикрепленные файлы должны быть размещены в письме в формате base64. Преобразовать файл в этот формат можно при помощи PHP-функции base64_encode():

// Открываем файл для чтения в бинарном формате $file=fopen("file.zip", "rb"); // Считываем его в строку $str_file $str_file=fread($file,filesize("file.zip")); // Преобразуем эту строку в base64-формат $str_file=base64_encode($str_file);

Теперь переменную $str_file, которая содержит файл, можно вставлять в письмо.

Для окончательного закрепления материала напишем функцию, которая отправляет письмо в HTML-формате по указанному адресату с прикрепленным файлом:

/* $to - адрес получателя письма $from_mail - адрес отправителя письма $from_name - имя отправителя письма $subject - тема письма $message - само сообщение в HTML-формате $file_name - путь к файлу, который надо прикрепить к письму (это может быть имя файла, выбранного в поле ) */ function sendMail($to,$from_mail,$from_name,$subject,$message,$file_name) < $bound="spravkaweb-1234"; $header="From: '$from_name' n"; $header.="To: $ton"; $header.="Subject: $subjectn"; $header.="Mime-Version: 1.0n"; $header.="Content-Type: multipart/mixed; boundary='$bound'"; $body="nn--$boundn"; $body.="Content-type: text/html; charset='windows-1251'\n"; $body.="Content-Transfer-Encoding: quoted-printablenn"; $body.="$message"; $file=fopen($file_name,"rb"); $body.="nn--$boundn"; $body.="Content-Type: application/octet-stream;"; $body.="name=".basename($file_name)."\n"; $body.="Content-Transfer-Encoding:base64n"; $body.="Content-Disposition:attachmentnn"; $body.=base64_encode(fread($file,filesize($file_name)))."n"; $body.="$bound--nn"; if(mail($to, $subject, $body, $header)) < echo "
Письмо было успешно отправлено!
";
> else 

Источник

Отдаем файлы эффективно с помощью PHP

Метод хорош тем, что работает с коробки. Надо только написать свою функцию отправки файла (немного измененный пример из официальной документации):

function file_force_download($file) < if (file_exists($file)) < // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) < ob_end_clean(); >// заставляем браузер показать окно сохранения файла header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); // читаем файл и отправляем его пользователю readfile($file); exit; > > 

Таким способом можно отправлять даже большие файлы, так как PHP будет читать файл и сразу отдавать его пользователю по частям. В документации четко сказано, что readfile() не должен создавать проблемы с памятью.

  • Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
  • Файл читается в внутренний буфер функции readfile(), размер которого составляет 8кБ (спасибо 2fast4rabbit)

2. Читаем и отправляем файл вручную

Метод использует тот же Drupal при отправке файлов из приватной файловой системы (файлы недоступны напрямую по ссылкам):

function file_force_download($file) < if (file_exists($file)) < // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) < ob_end_clean(); >// заставляем браузер показать окно сохранения файла header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); // читаем файл и отправляем его пользователю if ($fd = fopen($file, 'rb')) < while (!feof($fd)) < print fread($fd, 1024); >fclose($fd); > exit; > > 
  • Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
  • Позволяет сэкономить память сервера

3. Используем модуль веб сервера

3a. Apache

Модуль XSendFile позволяет с помощью специального заголовка передать отправку файла самому Apache. Существуют версии по Unix и Windows, под версии 2.0.*, 2.2.* и 2.4.*

В настройках хоста нужно включить перехват заголовка с помощью директивы:

Также можно указать белый список директорий, файлы в которых могут быть обработаны. Важно: если у Вас сервер на базе Windows путь должен включать букву диска в верхнем регистре.

Описание возможных опций на сайте разработчика: https://tn123.org/mod_xsendfile/

function file_force_download($file) < if (file_exists($file)) < header('X-SendFile: ' . realpath($file)); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); exit; >> 
3b. Nginx

Nginx умеет отправлять файлы из коробки через специальный заголовок.

Для корректной работы нужно запретить доступ к папку напрямую через конфигурационный файл:

Пример отправки файла (файл должен находиться в директории /some/path/protected):

function file_force_download($file) < if (file_exists($file)) < header('X-Accel-Redirect: ' . $file); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); exit; >> 

Больше информации на странице официальной документации

  • Скрипт завершается сразу после выполнения всех инструкций
  • Физически файл отправляется модулем самого веб сервера, а не PHP
  • Минимальное потребление памяти и ресурсов сервера
  • Максимальное быстродействие

Update: Хабраюзер ilyaplot дает дельный совет, что лучше слать не application/octet-stream , а реальный mime type файла. Например, это позволит браузеру подставить нужные программы в диалог сохранение файла.

Источник

Как отдать пользователю файл скриптом

Зачастую бывает необходимость в том, чтобы сайт умел отдавать файлы не просто на скачивание, а поддерживать возможность скачивания в несколько потоков и докачки файла в случае обрыва соединения.

Для начала попробуем просто отдать файл браузеру:

 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename color:#FF0000">.basename($filename).'"'); readfile($filename); ?>

В этом примере мы сформировали два заголовка для браузера, первый из которых сообщает ему о типе содержимого (в данном случае — поток каких-то байтов), а второй заставляет его выдать нам окно с именем файла для его сохранения на локальном диске.

С поддержкой докачки

Заголовок Accept-Ranges: bytes , отправленный сервером, сообщает клиенту о том, что он может запрашивать данные с сервера фрагментами, указывая их смещение в байтах.

Зная эту возможность, браузер может передать серверу смещение в байтах, с которого необходимо начать передачу файла. Для этого браузер посылает заголовок Range :

где 500 — смещение в байтах от начала файла.

Сервер в свою очередь устанавливает переменную окружения HTTP_RANGE и должен отправить заголовок

HTTP/1.1 206 Partial Content

который дает клиенту понять, что отдается часть контента. Далее сервер должен отдать клиенту ту часть контента, которую тот запрашивал с соответствующими заголовками:

Content-Type: application/octet-stream Content-Disposition: attachment; filename="имя_файла" Last-Modified: время_модификации_файла Accept-Ranges: bytes Content-Length: длина_отдаваемой_части Content-Range: bytes от-до/размер

Поясню последний заголовок на примере: имеем файл размером 10000 байт, отдаем все, кроме первых 500 байт. Тогда заголовок будет выглядеть так:

Content-Range: bytes 500-9999/10000
 // если файла нет if (!file_exists($filename))  header ('HTTP/1.0 404 Not Found'); exit; > // получим размер файла $fsize = filesize($filename); // дата модификации файла для кеширования $ftime = date("D, d M Y H:i:s T", filemtime($filename)); // смещение от начала файла $range = 0; // пробуем открыть $handle = @fopen($filename, "rb"); // если не удалось if (!$handle) header ('HTTP/1.0 403 Forbidden'); exit; > // если запрашивающий агент поддерживает докачку if ( isset($_SERVER['HTTP_RANGE']) )  $range = $_SERVER['HTTP_RANGE']; $range = str_replace('bytes=', '', $range); $range = str_replace('-', '', $range); // смещаемся по файлу на нужное смещение if ($range) fseek($handle, $range); > // если есть смещение if ($range)  header('HTTP/1.1 206 Partial Content'); > else  header('HTTP/1.1 200 OK'); > header('Content-Disposition: attachment; filename color:#FF0000">.basename($filename).'"'); header('Content-Type: application/octet-stream'); header('Last-Modified: '.$ftime); header('Accept-Ranges: bytes'); header('Content-Length: '.($fsize - $range)); header('Content-Range: bytes '.$range.'-'.($fsize - 1).'/'.$fsize); fpassthru($handle); fclose($handle); ?>

В несколько потоков

Если клиент скачивает в несколько потоков, он будет отправлять нам заголовки вида (для файла длиной 10000 байт):

  • Range: bytes=0-499 — первые 500 байт
  • Range: bytes=500-999 — вторые 500 байт
  • Range: bytes=-500 или Range: bytes=9500- — последние 500 байт
  • Range: bytes=500- или Range: bytes=-9500 — все, кроме первых 500 байт
  • Range: bytes=0-0 — только первый байт
  • Content-Range: bytes 0-499/10000 — отдаем первые 500 байт
  • Content-Range: bytes 500-999/10000 — отдаем вторые 500 байт
  • Content-Range: bytes 9500-9999/10000 — отдаем последние 500 байт
  • Content-Range: bytes 500-9999/10000 — отдаем все, кроме первых 500 байт
  • Content-Range: bytes 0-0/10000 — отдаем только первый байт
 // если файла нет if (!file_exists($filename))  header ('HTTP/1.0 404 Not Found'); exit; > // получим размер файла $fsize = filesize($filename); // дата модификации файла для кеширования $ftime = date("D, d M Y H:i:s T", filemtime($filename)); // пробуем открыть $handle = @fopen($filename, "rb"); // если не удалось if (!$handle) header ('HTTP/1.0 403 Forbidden'); exit; > // если запрашивающий агент поддерживает докачку if ( isset($_SERVER['HTTP_RANGE']) )  $range = $_SERVER['HTTP_RANGE']; $range = str_replace( 'bytes=', '', $range ); $range = explode( '-', $range ); if ( $range[0]=='0' && $range[1]=='0' )  // если bytes=0-0 $start = $stop = 0; > elseif ( !strlen( $range[0] ) )  // если bytes=-500 $start = $fsize - (int)$range[1]; $stop = $fsize - 1; > else  // если bytes=500-999 или bytes=500- $stop = (int)$range[1]; if ( !$stop ) $stop = $fsize - 1; // bytes=500- $start = (int)$range[0]; if ( $start ) fseek( $fd, $start ); > $length = $stop - $start + 1; header('HTTP/1.1 206 Partial Content'); header('Content-Disposition: attachment; filename color:#FF0000">.basename($filename).'"'); header('Content-Type: application/octet-stream'); header('Last-Modified: '.$ftime); header('Accept-Ranges: bytes'); header('Content-Length: ' . $length); header('Content-Range: bytes '.$start.'-'.$stop.'/'.$fsize); echo fread($handle, $length); > else  // запрашивающий агент не поддерживает докачку header('HTTP/1.1 200 OK' ); header('Content-Disposition: attachment; filename color:#FF0000">.basename($filename).'"'); header('Content-Type: application/octet-stream'); header('Last-Modified: '.$ftime); header('Accept-Ranges: bytes'); header('Content-Length: '.$fsize); fpassthru($handle); > fclose($handle); ?>

P.S. Этот код неполон, поскольку не обрабатывает мультидиапазонные запросы, когда клиент требует от сервера сразу несколько фрагментов:

Range: bytes=0-499,500-999,1000-1499

Источник

Читайте также:  Javascript target all classes
Оцените статью