PHP: Как переименовать файл, загруженный с помощью Zend_Form_Element_File?

Форма:

//excerpt
$file = new Zend_Form_Element_File('file');
$file->setLabel('File to upload:')
    ->setRequired(true)
    ->addValidator('NotEmpty')
    ->addValidator('Count', false, 1)
    ->setDestination(APPLICATION_UPLOADS_DIR);
$this->addElement($file);

контроллер:

//excerpt
if ($form->isValid($request->getPost()) {
    $newFilename = 'foobar.txt';
    //how should I rename the file?
    //When should I rename the file? Before or after receiving?
    try {
        $form->file->receive();
        echo 'filename: '. $form->file->getFileName();
    }
}

Вопросы:

  1. Когда я звоню $form->file->getFileName() он возвращает полный путь, а не только имя файла. Как я могу вывести только имя файла?

    //Answer: First, get an array of the parts of the filename:
    $pathparts = pathinfo($form->file->getFileName());
    //then get the part that you want to use
    $originalFilename = $pathparts['basename'];
    
  2. Как я могу переименовать имя файла в то, что я хочу? Можно ли это сделать с Rename фильтр? Я уже устанавливаю пункт назначения в форме, поэтому все, что я хочу сделать, это изменить имя файла. Может быть, мне не следует указывать пункт назначения в форме? Или, может быть, это не может быть сделано с фильтром. Может быть, я должен делать это с помощью функции PHP? Что я должен делать?

    //Answer: Use the rename filter:
    $form->file->addFilter('Rename', 'new-file-name-goes-here.txt');
    

Окончательное решение:

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

public function foobarAction()
{
    //...etc...

    if (!$form->isValid($request->getPost())) {
        $this->view->form = $form;
        return;
    }

    //the following will rename the file (I'm setting the upload dir in the form)
    $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();
    } catch (Exception $e) {
        //error: file couldn't be received, or saved (one of the two)
    }
}

7 ответов

Решение

Чтобы ответить на вопрос 1, чтобы получить имя файла из полного пути, вы можете использовать basename, или же pathinfo,

Например (копировать-вставить из документа):

$path = "/home/httpd/html/index.php";
$file = basename($path);         // $file is set to "index.php"

Или же:

$path_parts = pathinfo('/www/htdocs/index.html');
echo $path_parts['dirname'], "\n";
echo $path_parts['basename'], "\n";
echo $path_parts['extension'], "\n";
echo $path_parts['filename'], "\n"; // since PHP 5.2.0


Я полагаю, чтобы переименовать / переместить файл rename бы сделать свое дело, даже если это совсем не "Zend Framework решение".

Если файл не был перемещен ZF и все еще находится во временном каталоге, вы должны использовать move_uploaded_file - но, как вы используете setDestination Я полагаю, что файл больше не находится во временном каталоге системы.

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

Когда файл будет загружен через форму ниже, он будет переименован в "config.ini".

$form = new Zend_Form;
$form->setAction('/default/index/file-upload')
     ->setMethod('post');

$uploadFile = new Zend_Form_Element_File('uploadfile');
$uploadFile->addFilter(new Zend_Filter_File_Rename(
              array('target' => 'config.ini'))
           )
           ->setRequired(true)
           ->setLabel('Upload file:');

$form->addElement($uploadFile);
$form->addElement(new Zend_Form_Element_Submit('submit'));

if ($form->isValid($_POST)) {
    $values = $form->getValues();
}

Простое исправление, чтобы Zend переименовал перед загрузкой

Проблема, которую я рассматриваю здесь, объясняется более подробно здесь: http://www.thomasweidner.com/flatpress/2009/04/17/recieving-files-with-zend_form_element_file/

У меня были проблемы с получением файла для переименования перед загрузкой, и я нашел решение для моего сценария. В какой-то момент Зенд решил, что метод getValue() элемента file загружает файл для вас. К счастью, они добавили возможность отключить эту функцию.

Решение: если вы вызываете getValue() для элемента file или getValues ​​() в форме и хотите изменить имя перед его загрузкой, вы должны установить setValueDisabled(true) в своем Zend_Form_Element_File.

К вашему сведению: я не утверждаю, что это оптимизировано, я просто утверждаю, что это работает на меня

Создание элемента формы (магия внутри)

$uploadConfig = Zend_Registry::get('upload');
$fileuploader = new Zend_Form_Element_File('ugc_fileupload');
$fileuploader->setRequired(true);
$fileuploader->setLabel('*Upload File:');
$fileuploader->addValidator('Count', false, 1); // ensure only 1 file
$fileuploader->setValueDisabled(true); // ***THIS IS THE MAGIC***
$fileuploader->addValidator('Size', false, $uploadConfig['videomax']);
$fileuploader->addValidator('Extension', false, 'mov, avi, wmv, mp4');
$this->addElement($fileuploader, 'ugc_fileupload');

Переименовать перед загрузкой (внутри preUpload($form))

$uploadCfg = Zend_Registry::get('upload');

// Get the parts of the name
// Call to getValue() here was uploading the file before telling it not to!
$atiFile = $form->ugc_fileupload->getValue();
$fileExt = $this->getFileExtension($atiFile);
$nameBase = $this->getFileName($atiFile, $fileExt);
$fullName = $atiFile;
$fullPath = $uploadCfg['tmpdir'] . $fullName;

// Keep checking until the filename doesn't exist
$numToAdd = 0;
while(file_exists($fullPath)) {
  $fullName = $nameBase . $numToAdd . $fileExt;
  $fullPath = $uploadCfg['tmpdir'] . $fullName;
  $numToAdd++;
}

$upload = new Zend_File_Transfer_Adapter_Http();
// or $upload = $form->ugc_fileupload->getTransferAdapter();
// both work, I'm not sure if one is better than the other...

//Now that the file has not already been uploaded renaming works
$upload->addFilter(new Zend_Filter_File_Rename(array(
  'target' =>  $fullPath,
  'overwrite' => false)
));

try {
  $upload->receive();
} catch (Zend_File_Transfer_Exception $e) {
  //$e->getMessage()
}

Вспомогательные методы

public function getFileName($path, $ext) {
  return $bname = basename($path, $ext);
}

public function getFileExtension($path) {
  return $ext = strrchr($path, '.');
}

Это сложно сделать с Zend по нескольким причинам.

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

Например, скажем, у вас есть каталог назначения с именем / path / to / my / pics. Если два пользователя одновременно загрузят изображение под названием "me.png", они могут переопределить друг друга. Это потому, что фильтр переименования применяется ПОСЛЕ того, как файл перемещается в / path / to / my / pics. Таким образом, он не может быть переименован, пока не будет перезаписан новой загрузкой файла.

  1. Если вы используете фильтр переименования Zend, вы не сможете сохранить исходное расширение файлов.

Я сделал это следующим образом: 1. Расширьте адаптер передачи http, чтобы передать фильтру переименования исходное имя файла. Обычный адаптер передачи http передает временное имя в каталог tmp и не имеет расширения файла.

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

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

Причина, по которой я это сделал, заключается в том, что в моем каталоге назначения должна была быть одна картинка для каждого пользователя, где каждая картинка была названа 'user1.jpg' или 'user2.png'. Я хотел переименовать файл одновременно с перемещением, чтобы он не перекрывал другие файлы в каталоге, который я хотел сохранить.

Вот код, который я использовал.


class My_File_Transfer_Adapter_Http
    extends Zend_File_Transfer_Adapter_Http
{

    /**
     * Receive the file from the client (Upload)
     * This differs from the Zend adapter in that
     * the adapter passes in the files actual
     * name to the rename filter so that when
     * it is renamed, the renamer can use the extension
     * of the file and keep it or change it.
     *
     * @param  string|array $files (Optional) Files to receive
     * @return bool
     */
    public function receive($files = null)
    {
        if (!$this->isValid($files)) {
            return false;
        }

        $check = $this->_getFiles($files);
        foreach ($check as $file => $content) {
            if (!$content['received']) {
                $directory   = '';
                $destination = $this->getDestination($file);
                if ($destination !== null) {
                    $directory = $destination . DIRECTORY_SEPARATOR;
                }

                /******************************************/
                // The original transfer adapter
                // passes content['tmp_name']
                // but we'll pass in content['name'] instead
                // to have access to the extension
                /******************************************/


                $filename = $directory . $content['name'];
                $rename   = $this->getFilter('File_Rename');
                if ($rename !== null) {
                    $tmp = $rename->getNewName($content['name']);
                    if ($tmp != $content['name']) {
                        $filename = $tmp;
                    }

                    if (dirname($filename) == '.') {
                        $filename = $directory . $filename;
                    }

                    $key = array_search(get_class($rename), $this->_files[$file]['filters']);
                    unset($this->_files[$file]['filters'][$key]);
                }

                // Should never return false when it's tested by the upload validator
                if (!move_uploaded_file($content['tmp_name'], $filename)) {
                    if ($content['options']['ignoreNoFile']) {
                        $this->_files[$file]['received'] = true;
                        $this->_files[$file]['filtered'] = true;
                        continue;
                    }

                    $this->_files[$file]['received'] = false;
                    return false;
                }

                if ($rename !== null) {
                    $this->_files[$file]['destination'] = dirname($filename);
                    $this->_files[$file]['name']        = basename($filename);
                }

                $this->_files[$file]['tmp_name'] = $filename;
                $this->_files[$file]['received'] = true;
            }

            if (!$content['filtered']) {
                if (!$this->_filter($file)) {
                    $this->_files[$file]['filtered'] = false;
                    return false;
                }

                $this->_files[$file]['filtered'] = true;
            }
        }

        return true;
    }
}

Это адаптер, теперь для фильтра.


class My_Filter_File_Rename
    extends Zend_Filter_File_Rename
{

    /**
     * Internal array of array(source, target, overwrite)
     */
    protected $_files = array( );

    /**
     * Class constructor
     *
     * Options argument may be either a string, a Zend_Config object, or an array.
     * If an array or Zend_Config object, it accepts the following keys:
     * 'source'    => Source filename or directory which will be renamed
     * 'target'    => Target filename or directory, the new name of the sourcefile
     * 'overwrite' => Shall existing files be overwritten ?
     * 'keepExtension' => Should the files original extension be kept
     *
     * @param  string|array $options Target file or directory to be renamed
     * @param  string $target Source filename or directory (deprecated)
     * @param  bool $overwrite Should existing files be overwritten (deprecated)
     * @return void
     */
    public function __construct( $options )
    {
        if( $options instanceof Zend_Config )
        {
            $options = $options->toArray();
        }
        elseif( is_string( $options ) )
        {
            $options = array( 'target' => $options );
        }
        elseif( !is_array( $options ) )
        {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception( 'Invalid options argument provided to filter' );
        }

        if( 1 setFile( $options );
    }

    /**
     * Returns the files to rename and their new name and location
     *
     * @return array
     */
    public function getFile()
    {
        return $this->_files;
    }

    /**
     * Sets a new file or directory as target, deleting existing ones
     *
     * Array accepts the following keys:
     * 'source'    => Source filename or directory which will be renamed
     * 'target'    => Target filename or directory, the new name of the sourcefile
     * 'overwrite' => Shall existing files be overwritten ?
     * 'keepExtension' => Should the files original extension be kept
     *
     * @param  string|array $options Old file or directory to be rewritten
     * @return Zend_Filter_File_Rename
     */
    public function setFile( $options )
    {
        $this->_files = array( );
        $this->addFile( $options );

        return $this;
    }

    /**
     * Adds a new file or directory as target to the existing ones
     *
     * Array accepts the following keys:
     * 'source'    => Source filename or directory which will be renamed
     * 'target'    => Target filename or directory, the new name of the sourcefile
     * 'overwrite' => Shall existing files be overwritten ?
     * 'keepExtension' => Should the files original extension be kept
     *
     * @param  string|array $options Old file or directory to be rewritten
     * @return Zend_Filter_File_Rename
     */
    public function addFile( $options )
    {
        if( is_string( $options ) )
        {
            $options = array( 'target' => $options );
        }
        elseif( !is_array( $options ) )
        {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception( 'Invalid options to rename filter provided' );
        }

        $this->_convertOptions( $options );

        return $this;
    }

    /**
     * Returns only the new filename without moving it
     * But existing files will be erased when the overwrite option is true
     *
     * @param  string  $value  Full path of file to change
     * @param  boolean $source Return internal informations
     * @return string The new filename which has been set
     */
    public function getNewName( $value,
                                $source = false )
    {
        $file = $this->_getFileName( $value );
        if( $file[ 'source' ] == $file[ 'target' ] )
        {
            return $value;
        }

        if( !file_exists( $file[ 'source' ] ) && !$file['keepExtension'] )
        {
            return $value;
        }

        if( ($file[ 'overwrite' ] == true) && (file_exists( $file[ 'target' ] )) )
        {
            unlink( $file[ 'target' ] );
        }

        if( file_exists( $file[ 'target' ] ) )
        {
            require_once 'Zend/Filter/Exception.php';
            throw new Zend_Filter_Exception( sprintf( "File '%s' could not be renamed. It already exists.",
                                                      $value ) );
        }

        if( $source )
        {
            return $file;
        }

        return $file[ 'target' ];
    }

    /**
     * Defined by Zend_Filter_Interface
     *
     * Renames the file $value to the new name set before
     * Returns the file $value, removing all but digit characters
     *
     * @param  string $value Full path of file to change
     * @throws Zend_Filter_Exception
     * @return string The new filename which has been set, or false when there were errors
     */
    public function filter( $value )
    {
        $file = $this->getNewName( $value, true );
        if( is_string( $file ) )
        {
            return $file;
        }

        $result = rename( $file[ 'source' ], $file[ 'target' ] );

        if( $result === true )
        {
            return $file[ 'target' ];
        }

        require_once 'Zend/Filter/Exception.php';
        throw new Zend_Filter_Exception( sprintf( "File '%s' could not be renamed. An error occured while processing the file.",
                                                  $value ) );
    }

    /**
     * Internal method for creating the file array
     * Supports single and nested arrays
     *
     * @param  array $options
     * @return array
     */
    protected function _convertOptions( $options )
    {
        $files = array( );
        foreach( $options as $key => $value )
        {
            if( is_array( $value ) )
            {
                $this->_convertOptions( $value );
                continue;
            }

            switch( $key )
            {
                case "source":
                    $files[ 'source' ] = ( string ) $value;
                    break;

                case 'target' :
                    $files[ 'target' ] = ( string ) $value;
                    break;

                case 'overwrite' :
                    $files[ 'overwrite' ] = ( boolean ) $value;
                    break;
                case 'keepExtension':
                    $files[ 'keepExtension' ] = ( boolean ) $value;
                    break;
                default:
                    break;
            }
        }

        if( empty( $files ) )
        {
            return $this;
        }

        if( empty( $files[ 'source' ] ) )
        {
            $files[ 'source' ] = '*';
        }

        if( empty( $files[ 'target' ] ) )
        {
            $files[ 'target' ] = '*';
        }

        if( empty( $files[ 'overwrite' ] ) )
        {
            $files[ 'overwrite' ] = false;
        }

        if( empty( $files[ 'keepExtension' ] ) )
        {
            $files[ 'keepExtension' ] = true;
        }


        $found = false;
        foreach( $this->_files as $key => $value )
        {
            if( $value[ 'source' ] == $files[ 'source' ] )
            {
                $this->_files[ $key ] = $files;
                $found = true;
            }
        }

        if( !$found )
        {
            $count = count( $this->_files );
            $this->_files[ $count ] = $files;
        }

        return $this;
    }

    /**
     * Internal method to resolve the requested source
     * and return all other related parameters
     *
     * @param  string $file Filename to get the informations for
     * @return array
     */
    protected function _getFileName( $file )
    {
        $rename = array( );
        foreach( $this->_files as $value )
        {
            if( $value[ 'source' ] == '*' )
            {
                if( !isset( $rename[ 'source' ] ) )
                {
                    $rename = $value;
                    $rename[ 'source' ] = $file;
                }
            }

            if( $value[ 'source' ] == $file )
            {
                $rename = $value;
            }
        }

        if( !isset( $rename[ 'source' ] ) )
        {
            return $file;
        }

        if( !isset( $rename[ 'target' ] ) or ($rename[ 'target' ] == '*') )
        {
            $rename[ 'target' ] = $rename[ 'source' ];
        }

        if( is_dir( $rename[ 'target' ] ) )
        {
            $name = basename( $rename[ 'source' ] );
            $last = $rename[ 'target' ][ strlen( $rename[ 'target' ] ) - 1 ];
            if( ($last != '/') and ($last != '\\') )
            {
                $rename[ 'target' ] .= DIRECTORY_SEPARATOR;
            }

            $rename[ 'target' ] .= $name;
        }

        if( !is_dir( $rename['target'] ) || $rename[ 'keepExtension' ] )
        {
            $name = basename( $rename[ 'source' ] );
            $parts = explode( '.', $name );
            $extension = $parts[count( $parts ) - 1];

            $rename[ 'target' ] .= '.' . $extension;
        }
        return $rename;
    }

}

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



$fileElement->addPrefixPath('My_File_Transfer_Adapter', 'My/File/Transfer/Adapter', Zend_Form_Element_File::TRANSFER_ADAPTER );

$fileElement->addPrefixPath( 'My_Filter', 'My/Filter', Zend_Form_Element_File::FILTER );

Когда вы добавляете фильтр к элементу file, вам нужно будет сделать это следующим образом


$fileElement->addFilter(
                        'File_Rename',
                        array(
                            'target' => $this->_getPictureDestination() . DIRECTORY_SEPARATOR . "user$userId",
                            'overwrite' => true,
                            'keepExtension' => true
                        )
            )

Теперь, когда файлы будут перемещены в новый каталог, у них будет оригинальное расширение файлов, и у них будет новое имя, которое вы указали при добавлении фильтра в элемент file.

Если это было трудно понять, пожалуйста, дайте мне знать. Мне потребовалось некоторое время, чтобы выяснить, что происходит в Zend, чтобы сделать это, поэтому, если это кому-нибудь поможет, используйте этот код свободно.

// Для Zend Framework:: Переименовать загруженный файл

 $renameFile = 'newName.jpg';

 $fullFilePath = '/images/'.$renameFile;

 // Rename uploaded file using Zend Framework
 $filterFileRename = new Zend_Filter_File_Rename(array('target' => $fullFilePath, 'overwrite' => true));

 $filterFileRename -> filter($name);

Вы можете использовать фильтр переименования. Если вы хотите переименовать ваше имя файла во время загрузки, возможно, это поможет вам.

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

public function simple_fileformat($str)
{
    $str = preg_replace("{[^A-Za-z0-9_]\.}", "", $str);
    $str = str_replace(" ", "_", $str);
    return $str;
}

После этого вы можете использовать вышеуказанную функцию для переименования.

$filename = new Zend_Form_Element_File("filename");
$filename->setLabel("filename");
$filename->setRequired(true);
$filename->setDestination($doc_path);
$filename->addFilter("rename", $doc_path . DIRECTORY_SEPARATOR . $this->simple_fileformat(basename($filename->getFileName())));

Это просто. Не так ли?

Другие вопросы по тегам