PHPLover PHPLover - 4 years ago 131
PHP Question

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?

I've simple HTML File control/s on my form. It's dynamic in nature, means user can upload one or multiple files. Its HTML is as follows :

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


If I upload two images using above HTML file control and submit the form I get following in
$_FILES
array (output of
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
)

)

)


Now according to the new requirement I've to use Filepicker instead of simple HTML File control/s to upload files to the server. But the issue I'm facing is that further code logic and manipulation has already been written considering the above array's(
$_FILES
) content.

So, I want to convert the request coming through Filepicker into the same format as of array
$_FILES
so that there would be no need to make any change in already written code logic and manipulations.

Let me explain you the scenario more clearly, if user selects one or more image files from Google Drive via Filepicker and submits the form, the request will contain URLs of those images which are generated by Filepicker and the file names.

I want to use this data and form the array as of above array(
$_FILES
).

Answer Source

Assuming you are using PHP 5.2.1 or higher and can use the HTTPS stream wrapper in copy() and file_get_contents(), this function should be all you need:

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

This function takes an array of tokens (such as hFHUCB3iTxyMzseuWOgG) as argument.
You can call it like

getFilepickerFiles(array('hFHUCB3iTxyMzseuWOgG'));

I don't know exactly what Filepicker passes to your server, but if it is a full file URL like

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

then you can extract the tokens like this:

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

You could also put that right into getFilepickerFiles() to make it take an array of file URLs instead:

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

Explanation

I feel like the above code is rather straightforward, but here's how getFilepickerFiles() works (you should have read the Rest API documentation before reading this):

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

Initialize $files to an array like $_FILES containing no files.

$tmpdir = sys_get_temp_dir();

Get the directory where temporary files are stored, because we're gonna download the files to there (this function requires PHP 5.2.1 or higher).

foreach($urls as $url)

What foreach does should be clear.

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

Build our temporary file path, following the pattern of $_FILES (i.e. the path of the temporary files folder, the string "php", and some random characters).
That name we assign to $tmp (for easy later use) and we add it to the list of file paths.

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

Attempt to download the file to $tmp by using copy() with a URL as source.
The value returned by copy() is TRUE on success and FALSE on failure.
The error values present in $_FILES are UPLOAD_ERR_OK on success and any other value otherwise (source, I am going with UPLOAD_ERR_NO_FILE here in case of failure).
So in order to assign a meaningful error value, we use the ternary operator to add UPLOAD_ERR_OK to the list of error codes if copy() returns TRUE, and UPLOAD_ERR_NO_FILE otherwise.

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

Query the file's size and add it to the list of file sizes.

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

Get file metadata by using an URL as argument to file_get_contents(), which should return a JSON array that we decode into an associative array using json_decode(/*...*/, TRUE).
Since we appended &filename=true&mimetype=true to the end of the URL, we will only get the values filename and mimetype - we don't need all the rest.
The decoded array we assign to $meta;

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

Add the values filename and mimetype from the just decoded JSON array to the lists of file names and mime types respectively.

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

Return an array with the image key pointing to the array of files we created.

And we're done.


Demo? :(

I am not going to build an entire file hosting website for this, because it would take five times the amount of time I needed to write this answer.
So I'm afraid I can't provide you with a fully working live demo.

Unfortunately, neither 3v4l nor codepad have the HTTPS stream wrapper enabled, so I am not even able to provide you with a "see-for-yourself" proof of concept demo.

The best I can do is probably a screenshot of my terminal window (click to enlarge):

enter image description here

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download