1. PHP / Говнокод #16697

    +155

    1. 1
    2. 2
    3. 3
    4. 4
    5. 5
    6. 6
    <?php
    
    $data = file_get_contents("/path/to/photo.jpg"); // Read the file's contents
    $name = 'myphoto.jpg';
    
    force_download($name, $data);

    Я знаю конечно что это не говнокод, но последствия будут ужасными если не передать сразу же в функцию данные (т.к. PHP будет копировать содержимое дважды), представьте себе файл в 2МБ и много запросов, сервер капут в два раза быстрее.
    - https://ellislab.com/codeigniter/user-guide/helpers/download_helper.html

    Запостил: volter9, 15 Сентября 2014

    Комментарии (21) RSS

    • Да тут по-хорошему нужно отдать код 302/301 и заголовок Location:, чтобы файл клиенту отдал сервер, тогда пыху не придётся его читать.

      Всё-таки file_get_contents для тех случаев, когда содержимое нужно обработать.
      Ответить
      • Спасибо за сей полезный совет! Даже и не думал о таком )
        Ответить
      • Проверил, не работает. Весь прикол скрипта это спрятать путь файла и скачать файл. По вашей версии (301/302 + Location:) не получается во-первых скачать, во-вторых спрятать, а в-третьих теперь браузер думает что /test/ это есть /my/path/to/file.png (кэш наверное стоит очистить).
        Если вы знаете как это сделать (спрятать путь + скачать файл) через ваш способ то, может расскажите? )
        Ответить
        • А если сделать из пути нечитаемое говно?

          http://cs405518.userapi.com/v405518237/24cf/2ul6NxnHApk.jpg
          Во-первых, такой путь не будет обнаружен. Во-вторых, PHP не надо будет загружать и отдавать -- PHP не будет нагружать сервер.

          Ещё, наверное, если всё секьюрно, можно перемещать файл в папку с нечитаемым именем только на небольшое время. Или менять конфиги сервера так, чтобы, из URL /24cf/2ul6NxnHApk указывал на нужную папку.
          Но я не знаток в области ИБ, могу врать.
          Ответить
          • Няша )

            А вообще так тоже сойдет.

            > Ещё, наверное, если всё секьюрно, можно перемещать файл в папку с нечитаемым именем только на небольшое время. Или менять конфиги сервера так, чтобы, из URL /24cf/2ul6NxnHApk указывал на нужную папку.
            Но я не знаток в области ИБ, могу врать.

            А вообще можно крону задавать каждый час менять всем файлам имена. Но это фантазии и извращения. Вообщем можно что угодно придумать. Спасибо за ответ!
            Ответить
            • В принципе, можно и по крону менять файлам имена (хотя это и извращение). Способ неуниверсальный (нужны определённые права доступа), но зато в этом способе будет работать докачка (в других способах докачку придётся реализовать самому).

              P.S. Выяснилось, что у http_send_file докачка работает.
              Ответить
            • > Няша )
              А то! Я тщательно картинку выбирал.

              https://www.google.ru/search?q=userapi.com+няша&tbm=isch - вроде бы няши, да не все.
              Ответить
              • > Я тщательно картинку выбирал.
                Что-то боязно открывать картинку...
                Ответить
                • Спасибо зато в это хуже всего, в области ИБ, могут спрятать хоть рипы фильмов. Во-первых, исполнять не целиком (как это файл нельзя было скачиваемого фантазии и извращения. Вообще могу врать.

                  А картинка нормальная. Ничего плохого. Честно.
                  Ответить
              • userapi.com
                Почему ридеректит на вк?
                Ответить
                • Потому что это один из доменов Вконтактика, а именно служебный для хранения экмаскриптов и изображений.
                  Ответить
            • > Няша
              где?
              Ответить
        • Должно работать, если в Location: указан URL, который файловый сервер способен отдать. Но этот метод, к сожалению, не позволяет спрятать этот URL. Да, браузер свяжет старый URL с новым.

          Рассмотрим другие варианты, которые могут спрятать URL:
          1. Настроить привязку URL к путям файлов в конфиге сервера (Apache/lighttpd/nginx). Недостатком метода является то, что привязка будет статической и что требуется доступ к конфигу (что невыполнимо на общем хостинге).

          Если я правильно понял, здесь нужна динамическая привязка, чтобы в следующий раз по этому URL файл нельзя было скачать, так?

          2. http://php.net/manual/ru/function.http-send-file.php

          Это именно то, что нужно. Во-первых, файл будет загружаться в память не целиком, а по частям, так что можно передавать хоть рипы фильмов. Во-вторых, исполнять передачу будет ядро пхп, а не высокоуровневый код. В-третьих, привязку можно делать динамической. И в-четвёртых, в учебном примере даже показано, как задать имя скачиваемого файла (http_send_content_disposition или просто послать заголовок Content-Disposition:).

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

          4. С помощью функции header послать серверу специальный заголовок, чтобы он отдал файл. Недостаток в том, что разные сервера требуют разный заголовок. Если установлен Apache, нужно послать X-SendFile:, если Nginx, то X-Accel-Redirect:, а если Lighttpd, то X-LIGHTTPD-send-file.

          Возможно, возникнет вопрос, какой заголовок Content-Type нужно послать. Если отправить header('Content-Type: application/octet-stream'); то у пользователя браузер покажет диалог загрузки вместо открытия файла в браузере. CodeIgniter же не предлагает выбора Content-Type, он его определяет по расширению файла.
          Ответить
          • Вот это уже другой разговор! :)
            Спасибо!
            Ответить
          • Голосую за X-Accel-Redirect. Можно авторизовать некий хэш (со ввернутым туда таймаутом) пыхой и если прошел - то делать внутренний редирект серверу. Серверу даем настоящий путь, юзер видит только хэш. Ну еще не забыть хедеры правильные приделать Content-Type и имя файла Content-Disposition, чтоб у юзера на компе было адекватное имя.
            Ответить
          • насчет http_send_file. Штука прелестная. Но вот лично у меня какое-то предвзятое отношение к pecl http - там автор напилил вторую версию (дока на php.net на первую), несовместиму с первой.

            http://devel-m6w6.rhcloud.com/mdref/http

            Я там вообще send file не нашел в доках. Про первую версию автор я так понимаю вообще забыл.

            Наговнокодил в неймспейсах, но сделал нечто монструозное и малоюзабельное. Завязывать свой проект на такую какашку че-то не хочется, лучше уж завязать на конкретный веб-сервак (nginx например).
            Ответить
            • Спасибо. Не ожидал такой подлости от разработчиков pecl_http: между первой и второй версией ничего общего. К счастью, 1.7.x до сих пор можно достать. К несчастью, нужно выяснять, какое именно расширение стоит на сервере, и если не то, то менять.

              Я не понимаю, как можно было такие разные проекты назвать одним именем.
              Ответить
            • Откопал-таки пример, как послать клиенту файл в pecl_http 2:
              http://devel-m6w6.rhcloud.com/mdref/http/Env/Response/setContentDisposition

              Энтерпрайз эдишн.

              И судя по Accept-Ranges: в ответе даже докачка должна работать, как и в первой версии. Только вместо имени нужно отправлять дескриптор предварительно открытого файла.
              Ответить
    • Фрагмент кода CodeIgniter'а, который можно публиковать как говнокод:
      function force_download($filename = '', $data = '')
      	{
      		if ($filename == '' OR $data == '')
      		{
      			return FALSE;
      		}
      
      		// Try to determine if the filename includes a file extension.
      		// We need it in order to set the MIME type
      		if (FALSE === strpos($filename, '.'))
      		{
      			return FALSE;
      		}
      
      		// Grab the file extension
      		$x = explode('.', $filename);
      		$extension = end($x);
      
      		... пропустил неинтересную часть...
      
      		// Set a default mime if we can't find it
      		if ( ! isset($mimes[$extension]))
      Итак, на входе есть содержимое файла. Но CodeIgniter определяет MIME-тип не по содержимому, а по расширению и даже запрещает отправку файла, полное имя которого не содержит точки.
      Ответить

    Добавить комментарий