douyi4912 2016-06-07 14:21
浏览 136
已采纳

如果临时文件不是由PHP创建的,则move_uploaded_file不起作用

move_uploaded_file() does not work if I create a file using tempnam.

I am using Yii2's UploadedFile::getInstance() method to create a file instance. Now, the difference here is the fact that I am populating $_FILES like the following, before making the instance:

function addToFiles($key, $model, $url) {   
    $tempName = tempnam(ini_get('upload_tmp_dir'), 'php_files');
    $originalName = basename(parse_url($url, PHP_URL_PATH));
    $curl = new \common\components\Curl();
    $imgRawData = $curl->get($url);
    $fp = fopen($tempName, 'w');
    fwrite($fp, $imgRawData);
    fclose($fp);

    $_FILES[$model] = array(
        'name' => [$key => $originalName],
        'type' => [$key => 'image/jpeg'],
        'tmp_name' => [$key => $tempName],
        'error' => [$key => 0],
        'size' => [$key => strlen($imgRawData)],
    );
}

Then, I create an instance like following:

 $file = UploadedFile::getInstance($instance, $attr);

Dump of the $file:

yii\web\UploadedFile#1
(
    [name] => '20421_33_t.jpg'
    [tempName] => 'D:\\xampp\\tmp\\php8005.tmp'
    [type] => 'image/jpeg'
    [size] => 2527
    [error] => 0
)

Now, if I call:

$file->saveAs($folder . '/' . $filename);

It just returns false, without any warning or exception.

Now, I did some digging around and found out that Yii uses move_uploaded_file() (without any error suppression operator) in saveAs() method. The following is the core saveAs() method.

public function saveAs($file, $deleteTempFile = true)
{
    if ($this->error == UPLOAD_ERR_OK) {
        if ($deleteTempFile) {
            return move_uploaded_file($this->tempName, $file);
        } elseif (is_uploaded_file($this->tempName)) {
            return copy($this->tempName, $file);
        }
    }
    return false;
}

As per move_uploaded_file() documentation a warning should have been issued when return is false, but I get nothing! Error reporting + Yii2's debugger is enabled in my setup and it should break out on slightest of the error!

Question is- Is this by design? Or is this a bug? Or perhaps I am doing something horribly wrong?

By the way, I am not really looking for an alternative solution because I have it already. I am curious to find out why this does not work, more than anything.

Things to note:

  • $folder is writable. is_dir($folder) && is_writable($folder) returns true.

  • $file->tempName exists and not readonly! readfile($file->tempName) reads the file without a problem.

  • I even tried chmod($file->tempName , 0777); but it doesn't work.

  • The same method works perfectly if I do not use addToFiles method, and upload the same image file from the form. In such cases, the dump of the instance is exactly the same ( only the tempName is different obviously, e.g. D:\\xampp\\tmp\\phpDBCC.tmp), but the file uploads.

  • 写回答

2条回答 默认 最新

  • douyan1903 2016-06-07 14:44
    关注

    This is expected and you need to find some other way.

    Issue one: You should not modify $_* super globals. There are different things which can break. The is code which expects them to be immutable. If you're lucky you can get through this, but don't change it, but find a way to abstract from those files and mock the abstraction.

    Issue two: move_uploaded_files() is a special purpose function. The purpose is to bypass security measurements (like open_basedir or, historic, safe_mode) to access specific files owned by PHP's internals. This is needed as the uploaded file is received from the client before PHP can handle the request and properly identify the script to run. This function is not supposed to be used with other files except those created by PHP on the start of the specific request.

    To provide a proper solution you have to elaborate what you actually want to achieve. My guess is that you want to get data from somewhere else and don't want to have a broken file. or that purpose do something like this:

    $tempName = tempnam($folder, 'php_files');
    /* .... */
    $fp = fopen($tempName, 'w');
    /* ... */
    fclose($fp);
    rename($tempName, $folder . '/' . $filename);
    

    Mind that I directly write to $folder. This ensures that we're using the same device thus we while storing the file we see that we have the needed rights and enough disk space. The rename() then is very fast as we're on the same device.

    This still isn't perfect, as the script might crash in between for any reason. This would leave the temporary file there. So you'd need a cleanup routine.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥15 stable diffusion
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘