Что делать с хранением пользовательских загруженных файлов в приложении Zend Framework? - PullRequest
3 голосов
/ 04 декабря 2009

Я создаю веб-приложение, которое включает обычную загрузку и извлечение файлов (PDF-файлы, документы Word и т. Д.).

Вот требования:

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

Вопрос № 1: Где мне хранить эти файлы?

  1. В файловой системе?

    • Означает ли это каталог uploads в моем каталоге public?
    • Я также хочу, чтобы мне было легко развернуть новую версию моего приложения после внесения изменений в код. В настоящее время у меня есть процесс, когда я загружаю свое приложение в каталог с текущей датой (т.е. 2009-12-01) и создаю ссылку на него с названием current. Это будет означать, что каталог uploads должен находиться за пределами этого каталога, чтобы его можно было использовать для всех различных версий приложения, независимо от того, какая из них используется в данный момент.
  2. В базе данных?

    • Я мог бы хранить содержимое файла в базе данных и подавать его, когда пользователь запрашивает их загрузку. Это защитит файлы в моем приложении от неавторизованных пользователей, но я смогу достичь того же результата, когда создам контроллер для этой работы.
    • Это решит проблему с символьной ссылкой.
    • Однако это может быть плохой практикой (хранение содержимого файла в базе данных в дополнение к информации).

Как вы думаете, что я должен делать?

Решение:

Вот что я в итоге сделал:

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

/server/myapp/releases/2009-12-15
/server/myapp/current -> /server/myapp/releases/2009-12-15

Я создал каталог над каталогом своего приложения и в нем указала символическая ссылка на этот каталог:

/server/uploads
/server/myapp/current/application/data/uploads -> /server/uploads

Я определил переменную APPLICATION_UPLOADS_DIR в своем файле /public/index.php:

// Define path to uploads directory
defined('APPLICATION_UPLOADS_DIR')
    || define('APPLICATION_UPLOADS_DIR', realpath(dirname(__FILE__) . '/../data/uploads'));

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

<?php

class Default_Form_UploadFile extends Zend_Form
{
    public function init()
    {
        //excerpt

        $file = new Zend_Form_Element_File('file');
        $file->setLabel('File to upload:')
            ->setRequired(true)
            ->setDestination(APPLICATION_UPLOADS_DIR);
    }
}

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

public function uploadAction()
{
    //...excerpt...form has already been validated

    $originalFilename = pathinfo($form->file->getFileName());
    $newFilename = 'file-' . uniqid() . '.' . $originalFilename['extension'];
    $form->file->addFilter('Rename', $newFilename);

    try {
        $form->file->receive();
        //upload complete!

        $file = new Default_Model_File();
        $file->setDisplayFilename($originalFilename['basename'])
            ->setActualFilename($newFilename)
            ->setMimeType($form->file->getMimeType())
            ->setDescription($form->description->getValue());
        $file->save();

        return $this->_helper->FlashMessenger(array('success'=>'The file has been uploaded.'));

    } catch (Exception $exception) {
        //error
    }
}

Для загрузки файлов я создал действие загрузки, которое извлекает файл и отправляет его пользователю с помощью Помощника действий SendFile от Noginn .

public function downloadAction()
{
    //user already has permission to download the file
    $this->_helper->layout()->disableLayout(); //won't work if you don't do this
    $file = $this->_getFileFromRequest();
    $location = APPLICATION_UPLOADS_DIR . '/' . $file->getActualFilename();
    $mimeType = $file->getMimeType();
    $filename = $file->getDisplayFilename();
    $this->_helper->sendFile($location, $mimeType, array(
        'disposition' => 'attachment',
        'filename' => $filename
    ));
}

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

<a href="<?php echo $this->url(array(
    'controller'=>'files',
    'action'=>'download',
    'file'=>$file->id)); ?>"><?php echo $file->displayFilename; ?></a>

Вот и все! Если у вас есть другие советы или критические замечания по поводу этого метода, пожалуйста, оставьте свои ответы / комментарии.

Ответы [ 3 ]

2 голосов
/ 04 декабря 2009

Чтобы позволить пользователям загружать файлы напрямую, есть два решения:

  • хранит ваши файлы в подкаталоге вашего сайта (что вы уже делаете) , что часто требуется приложениям
  • храните ваши файлы в:
    • еще один отдельный виртуальный хост, который обслуживает только эти статические файлы (там нет версий)
    • Или, я полагаю, Apache Alias тоже подойдет - пример в руководстве по Apache немного похож на вашу ситуацию ^^


О вашем втором замечании с версионными каталогами: я часто делаю:

  • хранить файлы в каталоге, который не находится в каталоге приложения (например, /var/www); скажем /var/media
  • создать ссылку на символическую ссылку, которая указывает на этот каталог:
    • /var/www/media указывает на /var/media
    • это можно сделать с помощью чего-то вроде ln -s /var/media /var/www/media
  • там создание этой символической ссылки происходит довольно быстро и может выполняться каждый раз, когда у вас появляется новая версия приложения
    • и у вас может быть несколько версий приложения, которые все указывают на один и тот же каталог.

Это хорошо работает, легко развертывается и «обманывает» приложение, заставляя думать, что файлы находятся в одном из его подкаталогов - даже если это не физически.

1 голос
/ 05 декабря 2009

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

1 голос
/ 05 декабря 2009

Ссылка на каталог вне веб-корня, как предложил Паскаль, является хорошим решением. Один из способов сделать это, особенно если вам нужен какой-то контроль доступа пользователей к файлам, - это использовать php-скрипт для обслуживания этих файлов. У вас может быть контроллер загрузки / действие и т. Д., Который принимает имя файла в качестве параметра, затем загружает файл из некоторого каталога за пределами веб-корня и выводит его. Затем вам также необходимо отправить несколько заголовков перед самим файлом, чтобы браузер знал, что с ним делать. Заголовки могут быть установлены для конкретного типа файла или просто для общего «потока октетов», чтобы браузер отображал диалог загрузки.

...