如何将通过 Filepicker 发出的文件上传请求转换为使用 PHP 中的简单 HTML 文件控件发出的类似请求?

Posted

技术标签:

【中文标题】如何将通过 Filepicker 发出的文件上传请求转换为使用 PHP 中的简单 HTML 文件控件发出的类似请求?【英文标题】:How to convert the file/s upload request made through Filepicker into the similar request made by using simple HTML File control/s in PHP? 【发布时间】:2015-07-10 09:23:10 【问题描述】:

我的表单上有简单的 html 文件控件。它本质上是动态的,意味着用户可以上传一个或多个文件。它的 HTML 如下:

<input type="file" id="image_book" class="image_book upload" name="image[]" accept="image/*" value=""/>

如果我使用上面的 HTML 文件控件上传两张图片并提交我在 $_FILES 数组中得到的表单(print_r($_FILES); 的输出):

Array
(
    [image] => Array
        (
            [name] => Array
                (
                    [0] => Aurbd-b3582991-large.jpg
                    [1] => BL_Miller_GD_Square_KV_300dpi_A2.jpg
                )

            [type] => Array
                (
                    [0] => image/jpeg
                    [1] => image/jpeg
                )

            [tmp_name] => Array
                (
                    [0] => /tmp/phpSt8CJJ
                    [1] => /tmp/phpibFjR2
                )

            [error] => Array
                (
                    [0] => 0
                    [1] => 0
                )

            [size] => Array
                (
                    [0] => 519179
                    [1] => 86901
                )

        )

)

现在根据新要求,我必须使用 Filepicker 而不是简单的 HTML File control/s 将文件上传到服务器。但我面临的问题是,考虑到上述数组的($_FILES) 内容,已经编写了进一步的代码逻辑和操作。

因此,我想将来自 Filepicker 的请求转换为与数组 $_FILES 相同的格式,这样就无需对已编写的代码逻辑和操作进行任何更改。

让我更清楚地解释一下这个场景,如果用户通过 Filepicker 从 Google Drive 中选择一个或多个图像文件并提交表单,请求将包含由 Filepicker 生成的那些图像的 URL 和文件名。

我想使用这些数据并形成上述数组($_FILES)的数组。

【问题讨论】:

如果你能提供你的文件选择器集成代码会有所帮助,因为有不同的方法可以做到这一点,如using widget、javascript Pick Multiple、javascript pick and store 等,答案可能因不同而有所不同集成方式 【参考方案1】:

假设您使用的是 PHP 5.2.1 或更高版本,并且可以在 copy()file_get_contents() 中使用 HTTPS 流包装器,那么这个函数应该是您所需要的:

function getFilepickerFiles($tokens)

    $files = array('name' => array(),
                   'type' => array(),
                   'tmp_name' => array(),
                   'error' => array(),
                   'size' => array());
    $tmpdir = sys_get_temp_dir();
    foreach($tokens as $token)
    
        $files['tmp_name'][] = $tmp = $tmpdir.'/php'.$token;
        $files['error'][] = copy('https://www.filepicker.io/api/file/'.$token, $tmp) ? UPLOAD_ERR_OK : UPLOAD_ERR_NO_FILE;
        $files['size'][] = filesize($tmp);
        $meta = json_decode(file_get_contents('https://www.filepicker.io/api/file/'.$token.'/metadata?filename=true&mimetype=true'), TRUE);
        $files['name'][] = $meta['filename'];
        $files['type'][] = $meta['mimetype'];
    
    return array('image' => $files);

这个函数接受一个标记数组(例如hFHUCB3iTxyMzseuWOgG)作为参数。 你可以这样称呼它

getFilepickerFiles(array('hFHUCB3iTxyMzseuWOgG'));

我不知道 Filepicker 传递给您的服务器的确切内容,但如果它是一个完整的文件 URL,例如

https://www.filepicker.io/api/file/hFHUCB3iTxyMzseuWOgG

然后你可以像这样提取标记:

$tokens = array();
foreach($urls as $url)

    $matches = array();
    preg_match('# ^https://www\\.filepicker\\.io/api/file/([^/]*)/?', $url, $matches);
    $tokens[] = $matches[1];

// Pass $tokens to getFilepickerFiles()

您也可以将其直接放入 getFilepickerFiles() 以使其采用文件 URL 数组:

function getFilepickerFiles($urls)

    $files = array('name' => array(),
                   'type' => array(),
                   'tmp_name' => array(),
                   'error' => array(),
                   'size' => array());
    $tmpdir = sys_get_temp_dir();
    foreach($urls as $url)
    
        $matches = array();
        preg_match('# ^https://www\\.filepicker\\.io/api/file/([^/]*)/?', $url, $matches);
        $token = $matches[1];
        $files['tmp_name'][] = $tmp = $tmpdir.'/php'.$token;
        $files['error'][] = copy('https://www.filepicker.io/api/file/'.$token, $tmp) ? UPLOAD_ERR_OK : UPLOAD_ERR_NO_FILE;
        $files['size'][] = filesize($tmp);
        $meta = json_decode(file_get_contents('https://www.filepicker.io/api/file/'.$token.'/metadata?filename=true&mimetype=true'), TRUE);
        $files['name'][] = $meta['filename'];
        $files['type'][] = $meta['mimetype'];
    
    return array('image' => $files);


说明

我觉得上面的代码相当简单,但getFilepickerFiles() 的工作原理如下(在阅读本文之前,您应该阅读Rest API documentation):

$files = array('name' => array(),
               'type' => array(),
               'tmp_name' => array(),
               'error' => array(),
               'size' => array());

$files 初始化为像$_FILES 这样不包含文件的数组。

$tmpdir = sys_get_temp_dir();

获取存储临时文件的目录,因为我们要将文件下载到那里(此功能需要 PHP 5.2.1 或更高版本)。

foreach($urls as $url)

foreach 做了什么应该很清楚。

$files['tmp_name'][] = $tmp = $tmpdir.'/php'.$token;

按照$_FILES的模式构建我们的临时文件路径(即临时文件夹的路径,字符串“php”和一些随机字符)。 我们分配给$tmp 的名称(以便以后使用)并将其添加到文件路径列表中。

$files['error'][] = (int)(!copy('https://www.filepicker.io/api/file/'.$token, $tmp));

尝试使用copy() 将文件下载到$tmp,并将URL 作为源。copy() 返回的值是 TRUE 成功时和 FALSE 失败时。$_FILES 中出现的错误值是 UPLOAD_ERR_OK 成功,否则为任何其他值(source,如果失败,我将在这里使用 UPLOAD_ERR_NO_FILE)。 所以为了分配一个有意义的错误值,如果copy()返回TRUE,我们使用三元运算符将UPLOAD_ERR_OK添加到错误代码列表中,否则UPLOAD_ERR_NO_FILE

$files['size'][] = filesize($tmp);

查询文件大小并将其添加到文件大小列表中。

$meta = json_decode(file_get_contents('https://www.filepicker.io/api/file/'.$token.'/metadata?filename=true&mimetype=true'), TRUE);

通过使用 URL 作为 file_get_contents() 的参数来获取文件元数据,它应该返回一个 JSON 数组,我们使用 json_decode(/*...*/, TRUE) 将其解码为关联数组。 由于我们将 &amp;filename=true&amp;mimetype=true 附加到 URL 的末尾,因此我们只会得到值 filenamemimetype - 我们不需要其他所有值。 我们分配给$meta的解码数组;

$files['name'][] = $meta['filename'];
$files['type'][] = $meta['mimetype'];

将刚刚解码的 JSON 数组中的值 filenamemimetype 分别添加到文件名和 mime 类型列表中。

return array('image' => $files);

返回一个数组,其中image 键指向我们创建的文件数组。

我们完成了。


演示? :(

我不会为此建立一个完整的文件托管网站,因为编写此答案所需的时间是我的五倍。 所以恐怕我无法为您提供完整的现场演示。

不幸的是,3v4l 和 codepad 都没有启用 HTTPS 流包装器,所以我什至无法为您提供“自己看”的概念验证演示。

我能做的最好的可能是我的终端窗口的截图(点击放大):

【讨论】:

首先感谢您的帮助。我非常感谢您为解决我的问题并帮助我解决这个问题所做的努力。但我想要与 $_FILES 相同的输出。在您的尝试中,数组结构完全不同。数组开头缺少 ['image'] 键。您能否对您的代码进行必要的更改以添加键 ['image'] 并使结果数组类似于 $_FILES。再次感谢。 我试过你的代码。我还能够将数组转换为所需的格式。但又提出了一个新问题。处理 $files 数组(我们从文件选择器请求中获得的那个)后,文件没有上传到服务器。你遇到过同样的问题吗? 您的意思是,您的服务器无法从文件选择器服务器获取文件?不,这并没有发生在我身上,因为我的截图中下载文件的 MD5 哈希值应该可以证明。您是否启用了allow_url_fopen? 在转换 Filepicker 请求之前的一切对我来说都很好,但我无法将用户使用 Filepicker 选择的文件上传到服务器。你能在这方面帮助我吗?我试过了,但图像没有上传到服务器上的文件夹。另外我不明白你为什么使用 md5_file() 函数?这对上传文件/文件有帮助吗?如果你能在这方面帮助我,整个问题都会得到解决,现在我只在最后一点卡住了。再次感谢您向我伸出援助之手。热切地等待您的回复。 md5_file 只是为了证明下载文件的完整性。但是,“服务器”是模棱两可的,因为有两个:您的服务器和文件选择器服务器。哪个操作失败,从用户到文件选择器或从文件选择器到您?

以上是关于如何将通过 Filepicker 发出的文件上传请求转换为使用 PHP 中的简单 HTML 文件控件发出的类似请求?的主要内容,如果未能解决你的问题,请参考以下文章

HTML filepicker multi - 获取正在使用的文件

filepicker.io 的 Facebook 权限

如何使用操作查询发出多个 Alamofire 请求

FilePicker 以字符串形式返回名称和路径

如何在没有 FilePicker 的情况下在固定位置保存和加载 InkCanvas gif 文件

FilePicker / FileStack 错误:指定的策略不允许调用删除