Skip to main content

This site requires you to update your browser. Your browsing experience maybe affected by not having the most up to date version.

We've moved the forum!

Please use forum.silverstripe.org for any new questions (announcement).
The forum archive will stick around, but will be read only.

You can also use our Slack channel or StackOverflow to ask for help.
Check out our community overview for more options to contribute.

Customising the CMS /

Moderators: martimiz, Sean, Ed, biapar, Willr, Ingo, swaiba

Upload file through HTTP PUT : "File is not a valid upload"


Go to End


2 Posts   1413 Views

Avatar
fireincairo

Community Member, 4 Posts

17 March 2015 at 12:42am

Edited: 17/03/2015 12:48am

I am trying to set up a simple REST-like API with Silverstripe to manage files. In that perspective I would like to implement the main HTTP verbs : GET (get the file), POST (create a file), PUT (update a file) and DELETE (delete the file).

I managed to implement GET, POST and DELETE, but I am still struggling with PUT. It's very easy indeed to upload a file through POST, as PHP automatically sends the uploaded file in the $_FILES global variable, which can then be fed into the Upload::loadIntoFile() method.

However PHP doesn't seem to be so helpful with PUT requests, so I had to implement it by myself. I got the content of the file from the body of the HTTP request and tried to feed it to Upload::loadIntoFile() by emulating the format of the data in $_FILES :

class API_Controller extends Page_Controller {
    private static $url_handlers = array(
        '$upload!' => 'upload_file',
    );
    private static $allowed_actions = array(
        'upload_file',
    );
    public function upload_file(SS_HTTPRequest $request) {
        // Get the existing Silverstripe File object
        $myExistingFile = $this->getMyExistingFile();

        if ($request->isPUT()) {
            // Extract the file content from the HTTP request body
            $fileContent = $this->getFileContent($request->getBody());

            $upload = Upload::create();
            $tempfd = tmpfile();
            fwrite($tempfd, $fileContent);
            $tmpFilename = stream_get_meta_data($tempfd)['uri'];

            // Emulate the data format expected by Upload::loadIntoFile()
            $tmpFile = array(
                'name' => 'myfile.txt',
                'type' => 'text/plain',
                'tmp_name' => $tmpFilename,
                'error' => 0,
                'size' => filesize($tmpFilename)
            );
            $upload->loadIntoFile($tmpFile, $myExistingFile);
            if ($upload->isError()) {
                return new SS_HTTPResponse('FILE UPLOAD FAILED', 500);
            }
        }
    }
}

Unfortunately, loadIntoFile() always returns an error "File is not a valid upload". After having a look at the code and searching the internet, I found that this is because there is a validation performed during the upload with the PHP function is_uploaded_file(), and this function can tell that the file I am trying to upload doesn't come from a POST request.

Has anyone ever tried to implement PUT requests with Silverstripe ? Is there a recommended way to handle PUT requests ? Otherwise, how can I workaround the is_uploaded_file() validation ?

Here are some relevant parts of my PHP configuration (the file I am trying to upload is < 1KB) :
file_uploads = On
upload_max_filesize = 512M
max_file_uploads = 20

Avatar
fireincairo

Community Member, 4 Posts

30 March 2015 at 7:34pm

So I ended up writing the file content directly on the filesystem through file_put_contents().

Not the best solution, but I had to come up with something. I would be curious if anyone had a more Silverstripe-way to do it though ;)

    public function upload_file(SS_HTTPRequest $request) {
        // Get the existing Silverstripe File object
        $myExistingFile = $this->getMyExistingFile();

        if ($request->isPUT()) {
            // Extract the file content from the HTTP request body
            $fileContent = $this->getFileContent($request->getBody());

            // Write in the file on disk
            $r = file_put_contents($myExistingFile->getFullPath(), $fileContent);
            if (! $r) {
                return new SS_HTTPResponse('ERROR DURING FILE UPLOAD', 500);
            }
    }