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?
Asked Answered
C

1

10

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

Cero answered 30/4, 2015 at 15:25 Comment(1)
If you can provide your filepicker integration code that will help, as there are different method for doing this like using widget, Javascript Pick Multiple, javascript pick and store, etc. and answer can vary for this different way of integrationsAnarthria
G
6

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

Gustatory answered 2/5, 2015 at 19:56 Comment(8)
First of all thanks for your help. I really appreciate the efforts you put in to resolve my issue and help me out of this problem. But I want the same output as of $_FILES. In your attempt the array structure is quite different. The ['image'] key is missing at the beginning of array. Can you please make the necessary changes to your code to add the key ['image'] and make the resultant array similar to $_FILES. Thanks once again.Cero
I tried your code. I was also able to convert the array into the desired format. But again one new issue has raised. The file is not getting uploaded to the server after processing $files array(the one we got from filepicker request). Did you face the same issue?Cero
You mean, your server fails to fetch the file from the filepicker server? No, that did not happen to me, as the MD5 hash of the downloaded file in my screenshot is supposed to prove. Do you have allow_url_fopen enabled?Gustatory
Everything till conversion of Filepicker request works fine for me but I'm not able to upload the file/files selected by user using Filepicker to the server. Can you please help me in this regard? I tried for it but the images are not getting uploaded to the folder on server. Also I didn't get understand why you've used the md5_file() function? Does that help in uploading the file/files? If you could help me in this regard the whole problem will get resolve, now I got stuck at last point only. Thanks once again for providing me the helping hand. Waiting eagerly for your reply.Cero
md5_file was only to prove the downloaded file's integrity. "The server" is ambiguous though, because there are two: yours and the filepicker server. Which operation fails, the one from user to filepicker or from filepicker to you?Gustatory
I want to upload the images selected by user to my server, not the filepicker server. My ultimate goal is to allow users select image files using filepicker and save them on my server. How should I achieve this?Cero
Read the FAQ, point "Can I host your product on my servers?".Gustatory
Do you mean to say it's impossible to save the images uploaded by user using filepicker to my server?Cero

© 2022 - 2024 — McMap. All rights reserved.