Skip to content

[HttpFoundation] Handle file uploads with PUT #36409

Closed
@CoalaJoe

Description

@CoalaJoe

Description
I and probably a hand full of developers wasted hours debugging on why file uploads with PUT requests don't work. After finally ending up at https://www.php.net/manual/en/features.file-upload.put-method.php, it seems like PHP handled file uploads on PUT requests differently.

Example
I think it would be a great addition to the Request class to accommodate for this in the $request->files FileBag.

Current Situation

Here some awful code that showcases the current situation:

public function getUploadedFilesFromBody(): array
{
    // Try finding in PHP PUT content.
    // https://www.php.net/manual/en/features.file-upload.put-method.php

    $putdata = fopen("php://input", "r");
    $uploadedFiles = [];

    $temporaryFilePointer = null;
    $tmpFileName = null;
    $header = false;
    $fileKey = null;
    $fileName = null;
    $mimeType = null;
    $currentBoundary = null;

    while (false !== ($line = fgets($putdata))) {
        $webkitFormBoundaryFlag = substr($line, 0, 24) === '------WebKitFormBoundary';
        if (false === $header && $webkitFormBoundaryFlag) {
            $header = true;
            // Check on current file
            $currentBoundary = trim(substr($line, 24));

            // Read header information.
            $contentDisposition = fgets($putdata);
            if (false === $contentDisposition || 'Content-Disposition' !== substr($contentDisposition, 0, 19)) {
                // Wrong format.
                break;
            }
            $fileKeyGroups = [];
            $status = preg_match('/\ name=\"(?P<file>[\w\.]*)\"/', $contentDisposition, $fileKeyGroups);
            if (0 === $status) {
                // Could not extract name
                break;
            }
            $fileKey = $fileKeyGroups['file'];

            $fileNameGroups = [];
            $status = preg_match('/\ filename=\"(?P<filename>[\w\.]*)\"/', $contentDisposition, $fileNameGroups);
            if (0 === $status) {
                // Could not extract filename
                break;
            }
            $fileName = $fileNameGroups['filename'];

            $contentType = fgets($putdata);
            if (false === $contentType) {
                // Wrong format.
                break;
            }
            $mimeType = substr($contentType, strlen('Content-Type: '));
            $skip = fgets($putdata);
            if (false === $skip) {
                break;
            }

            $tmpFileName = tempnam(sys_get_temp_dir(), 'media_object_put_');
            $temporaryFilePointer = fopen($tmpFileName, "w");
        } elseif (true === $header && $webkitFormBoundaryFlag) {
            $headerEnd = substr($line, 24 + strlen($currentBoundary), 2) === '--';
            if ($headerEnd) {
                $header = false;
                // Create a UploadedFile.
                $uploadedFiles[$fileKey] = new UploadedFile($tmpFileName, $fileName, $mimeType);
                fclose($temporaryFilePointer);
            }
        } else {
            fwrite($temporaryFilePointer, $line);
        }
    }
    fclose($putdata);

    return $uploadedFiles;
}

This would create something like this:
image

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions