PHP:如何重命名使用 Zend_Form_Element_File 上传的文件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1883876/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 04:10:54  来源:igfitidea点击:

PHP: How to rename a file uploaded with Zend_Form_Element_File?

phpzend-frameworkfile-uploadzend-file

提问by Andrew

Form:

形式:

//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);

Controller:

控制器:

//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();
    }
}

Questions:

问题:

  1. When I call $form->file->getFileName()it returns the full path, not just the file name. How can I output just the name of the file?

    //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. How can I rename the filename to something I want? Can this be done with the Renamefilter? I'm already setting the destination in the form, so all I want to do is change the filename. Maybe I shouldn't be setting the destination in the form? Or maybe this can't be done with a filter. Maybe I should be doing this with a PHP function? What should I do?

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

Final Solution:

最终解决方案:

This is what I ended up doing:

这就是我最终做的:

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)
    }
}

采纳答案by Pascal MARTIN

To answer question 1, to get a filename from a full path, you can use basename, or pathinfo.

要回答问题 1,要从完整路径获取文件名,您可以使用basename, 或pathinfo

For example (copy-paste from the doc):

例如(从文档复制粘贴)

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

Or :

或者 :

$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


To rename / move the file, I suppose renamewould do the trick, even if it's quite not "Zend Framework solution".


要重命名/移动文件,我想rename可以解决问题,即使它不是“Zend Framework 解决方案”。

If the file has not been moved by ZF and is still in the temporary directory, you should use move_uploaded_file-- but as you are using setDestination, I suppose the file is no longer in the sytem's temporary directory.

如果该文件尚未被 ZF 移动并且仍在临时目录中,则您应该使用move_uploaded_file-- 但在您使用时setDestination,我想该文件不再位于系统的临时目录中。

回答by Elzo Valugi

回答by Matt Setter

Folks, here's a simple example of a form that uses the rename filteron a file after it's been uploaded. There are many more options and yes, you'd need to take in to consideration existing files, but to get you started, here you go.

伙计们,这是一个简单的表单示例,它在文件上传后对文件使用重命名过滤器。还有更多选择,是的,您需要考虑现有文件,但要让您开始,就在这里。

When the file's uploaded through the form below, it will be renamed to 'config.ini'.

当文件通过下面的表格上传时,它将被重命名为“ 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();
}

回答by xilplaxim

Easy fix to get Zend to rename before uploading

轻松修复让 Zend 在上传前重命名

The problem I address here is explained in more detail here: http://www.thomasweidner.com/flatpress/2009/04/17/recieving-files-with-zend_form_element_file/

我在这里解决的问题在这里有更详细的解释:http: //www.thomasweidner.com/flatpress/2009/04/17/recieving-files-with-zend_form_element_file/

I was having trouble getting the file to rename before uploading and found the solution for my scenario. At some point Zend thought it clever to have the getValue() method of the file element upload the file for you. Fortunately they added an option to disable this feature.

我在上传之前无法重命名文件,并找到了适合我的方案的解决方案。在某些时候,Zend 认为让 file 元素的 getValue() 方法为您上传文件很聪明。幸运的是,他们添加了一个选项来禁用此功能。

Solution:If you are calling getValue() on the file element, or getValues() on the form, and you want to modify the name before it uploads you have to set setValueDisabled(true) on your Zend_Form_Element_File.

解决方案:如果您在文件元素上调用 getValue() 或在表单上调用 getValues(),并且您想在上传之前修改名称,您必须在 Zend_Form_Element_File 上设置 setValueDisabled(true)。

Fyi: I don't claim this to be optimized, I just claim it to work for me

仅供参考:我并没有声称这是优化的,我只是声称它对我有用

Creating the form element(magic inside)

创建表单元素里面的魔法

$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');

Rename before uploading(inside preUpload($form))

上传前重命名(在 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()
}

Helper methods

辅助方法

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

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

回答by Jerry Saravia

This is difficult to do with Zend for a few reasons.

Zend 很难做到这一点,原因有几个。

  1. If you rename the file after it's been moved to the upload destination then it might've overwritten a file that you didn't want rewritten.
  1. 如果您在将文件移至上传目标后重命名该文件,则它可能覆盖了您不想重写的文件。

For example, say you have a destination directory called /path/to/my/pics. If two users, at the same time, upload a picture called 'me.png' then they might override each other. This is because the rename filter is applied AFTER the file is moved to /path/to/my/pics. Thus, it might not be renamed before it is overwritten by a new file upload.

例如,假设您有一个名为 /path/to/my/pics 的目标目录。如果两个用户同时上传一张名为“me.png”的图片,那么他们可能会相互覆盖。这是因为重命名过滤器是在文件移动到 /path/to/my/pics 之后应用的。因此,它可能不会在被新文件上传覆盖之前重命名。

  1. If you use Zend's rename filter then you can't keep the original files extension.
  1. 如果您使用 Zend 的重命名过滤器,那么您将无法保留原始文件扩展名。

The way I did it was to do the following, 1. Extend the http transfer adapter to pass the rename filter the original file name. The normal http transfer adapter passes in the temporary name in the tmp directory and does not have the file extension.

我这样做的方法是执行以下操作: 1. 扩展 http 传输适配器以通过重命名过滤器传递原始文件名。普通的http传输适配器传入的是tmp目录下的临时名称,没有文件扩展名。

  1. Extend the rename filter so that you can specify whether or not it should keep the original file extension.
  1. 扩展重命名过滤器,以便您可以指定它是否应保留原始文件扩展名。

Afterward, you'll have to add the prefix to the form you're using so that the form can find your adapter and so your adapter can find the new Rename filter you've made.

之后,您必须将前缀添加到您正在使用的表单中,以便表单可以找到您的适配器,并且您的适配器可以找到您创建的新重命名过滤器。

The reason I did it this way is because my destination directory was going to have one pic in it for every user where each pic was named 'user1.jpg' or 'user2.png'. I wanted to rename the file at the same time that I moved it so it wouldn't override any other files in the directory that I wanted to keep.

我这样做的原因是因为我的目标目录将为每个用户提供一张图片,其中每张图片都被命名为“user1.jpg”或“user2.png”。我想在移动文件的同时重命名它,这样它就不会覆盖我想保留的目录中的任何其他文件。

Here is the code I've used.

这是我使用过的代码。


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;
    }
}

That is the adapter, now for the filter.

那是适配器,现在是过滤器。


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;
    }

}

You'll then have to add the prefix path's to the file element that you've made to upload the file.

然后,您必须将前缀路径添加到您为上传文件而制作的文件元素中。



$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 );


When you add the filter to the file element you'll have to do it the following way

当您将过滤器添加到文件元素时,您必须按以下方式进行


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


Now, when the files get moved over to the new directory they'll have the original files extension and they'll have the new name that you specified when you added the filter to the file element.

现在,当文件移动到新目录时,它们将具有原始文件扩展名,并且它们将具有您在将过滤器添加到文件元素时指定的新名称。

If this was hard to understand please let me know. It took me a while to figure out what was going on in Zend to do this so if it helps anyone, use this code freely.

如果这很难理解,请告诉我。我花了一段时间才弄清楚 Zend 中发生了什么来执行此操作,因此如果它对任何人有帮助,请自由使用此代码。

回答by streetparade

// For Zend Framework :: Rename Uploaded File

// 对于 Zend 框架 :: 重命名上传的文件

 $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);

回答by aramisz

You can use Rename filter. If you want to rename your filename during uploading maybe it helps you.

您可以使用重命名过滤器。如果您想在上传过程中重命名文件名,也许对您有所帮助。

First we need a function rename or remove unwanted characters from your filename for example use this.

首先,我们需要一个函数重命名或从文件名中删除不需要的字符,例如使用它。

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

After that you can use the above function for renaming.

之后,您可以使用上述功能进行重命名。

$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())));

That is easy. Is not it?

那很容易。是不是?