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 使用ue5插件narrative时如何切换关卡也保存叙事任务记录
  • ¥20 软件测试决策法疑问求解答
  • ¥15 win11 23H2删除推荐的项目,支持注册表等
  • ¥15 matlab 用yalmip搭建模型,cplex求解,线性化处理的方法
  • ¥15 qt6.6.3 基于百度云的语音识别 不会改
  • ¥15 关于#目标检测#的问题:大概就是类似后台自动检测某下架商品的库存,在他监测到该商品上架并且可以购买的瞬间点击立即购买下单
  • ¥15 神经网络怎么把隐含层变量融合到损失函数中?
  • ¥15 lingo18勾选global solver求解使用的算法
  • ¥15 全部备份安卓app数据包括密码,可以复制到另一手机上运行
  • ¥20 测距传感器数据手册i2c