Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aws S3 CompleteMultipartUpload error

Fatal error: Uncaught Aws\S3\Exception\InvalidRequestException: AWS Error Code: InvalidRequest, Status Code: 400, AWS Request ID: B1A28EBE65521DF4, AWS Error Type: client, AWS Error Message: You must specify at least one part, User-Agent: aws-sdk-php2/2.7.22 Guzzle/3.9.2 curl/7.40.0 PHP/5.6.6 thrown in C:\vhosts********.com\db*****\FileUploader_v2\aws\Aws\Common\Exception\NamespaceExceptionFactory.php on line 91

I am getting the above error whenever I run my multipart upload program use AWS S3. My program should slice part of the file in a JS script that is then sent to a php script using XMLHTTPRequest. That part seems to be working well. However, the issue arises when CompleteMultipartUpload is called. What I gather from the error is that my part are too small or are empty.

upload.htm:

var command; var file; var ownerName; var totalSize; var partSize = 2 * 1024 * 1024; // constant var sendBackData; var totalSize; var sureUploadSize = 0, probableUplaodSize = 0; var numParts; var partsLeft = [];

    function _(el){
        return document.getElementById(el);
    }

    function calcTotalSize(file){
        var size_total = 0;
        for(var i = 0; i < file.length; i++){
            size_total += file[i].size;
        }
        return size_total;
    }

    function uploadFile(){
        file = _("file").files[0];
        console.log(file);
        ownerName = _("name").value;
        totalSize = file.size;
        command = 'CreateMultipartUpload';

        var formdata = new FormData();
        formdata.append("command", command);
        formdata.append("filename", file.name);
        formdata.append("name", ownerName);
        var ajax = new XMLHttpRequest();
        ajax.open("POST", "FileUploader.php", true);
        ajax.send(formdata);
        ajax.onreadystatechange = function() {
            if (ajax.readyState === 4) {
                sendBackData = JSON.parse(ajax.responseText);
                numParts = Math.ceil(totalSize / partSize);
                uploadPart(1);
            }
        };
    }

    function uploadPart(partNum){
        console.log("Uploading part " + partNum);
        console.log(sendBackData['uploadId']);
        command = 'UploadPart';

        if (partNum > numParts) {
            completeMultipartUpload();
            return;
        }

        var formdata = new FormData();

        var start = (partNum - 1) * partSize;
        var end = start + partSize;
        if (end > totalSize)
            end = totalSize;
        var length = end - start;
        var curBlobPart = file.slice(start, end);

        //console.log(sendBackData['uploadId']);

        formdata.append("file[]", curBlobPart);
        formdata.append("command", command);
        formdata.append("uploadId", sendBackData['uploadId']);
        formdata.append("key", sendBackData['key']);
        formdata.append("partNumber", partNum);

        var ajax = new XMLHttpRequest();
        ajax.open("POST", "FileUploader.php", true);
        ajax.addEventListener("load", completeHandler, false);
        ajax.addEventListener("error", errorHandler, false);
        ajax.addEventListener("abort", abortHandler, false);
        ajax.send(formdata);
        ajax.onreadystatechange = function() {
            if (ajax.readyState === 4) {
                uploadPart(partNum + 1);
            }
        };
    }

    function completeMultipartUpload() {
        command = 'CompleteMultipartUpload';

        var formdata = new FormData();
        formdata.append("command", command);
        formdata.append("uploadId", sendBackData['uploadId']);
        formdata.append("key", sendBackData['key']);

        var ajax = new XMLHttpRequest();
        ajax.open("POST", "FileUploader.php", true);
        ajax.addEventListener("load", completeHandler, false);
        ajax.addEventListener("error", errorHandler, false);
        ajax.addEventListener("abort", abortHandler, false);
        ajax.send(formdata);
        ajax.onreadystatechange = function() {
            if (ajax.readyState === 4) {
                alert("File uploaded successfully");
            }
        };
    }

    function progressHandler(event){
        _("loaded_n_total").innerHTML = "Uploaded "+event.loaded+" bytes of "+event.total;
        var percent = (event.loaded / event.total) * 100;
        _("progressBar").value = Math.round(percent);
        _("status").innerHTML = Math.round(percent)+"% uploaded... please wait";
    }
    function completeHandler(event){
        _("status").innerHTML = event.target.responseText;
        _("progressBar").value = 0;
    }
    function errorHandler(event){
        _("status").innerHTML = "Upload Failed";
    }
    function abortHandler(event){
        _("status").innerHTML = "Upload Aborted";
    }`

FileUploader.php: `

//require 'SimpleImage.php';
//require 'gifsplit.php';
//require 'functions.php';

require 'config.php';
require 'aws/aws-autoloader.php';

use Aws\Common\Exception\MultipartUploadException;
use Aws\S3\Model\MultipartUpload\UploadBuilder;
use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;

function sendJson($arr)
{
    header('Content-Type: application/json');
    die(json_encode($arr));
}

// S3
$s3 = S3Client::factory(array(
    'key' => AWS_KEY,
    'secret' => AWS_SECRET_KEY
));

//$part = $_POST['part'];

switch ($_POST['command']) {
    case 'CreateMultipartUpload':
        $key = "other/".$_POST['name']."/".$_POST['filename'];

        $response = $s3->createMultipartUpload(array(
            'Bucket'    => TMP_IMG,
            'Key'       => $key
        ));

        $uploadId = $response['UploadId'];

        sendJson(array(
            'uploadId' => $uploadId,
            'key'      => $key
        ));
        break;

    case 'UploadPart':
        var_dump($_FILES['file']);

        $result = $s3->uploadPart(array(
            'Bucket'    => TMP_IMG,
            'Key'       => $_POST['key'],
            'UploadId'  => $_POST['uploadId'],
            'PartNumber'=> $_POST['partNumber'],
            'Body'      => $_FILES['file']['tmp_name']
        ));
        break;

    case 'CompleteMultipartUpload':
        $partsModel = $s3->listParts(array(
            'Bucket' => TMP_IMG,
            'Key'       => $_POST['key'],
            'UploadId'  => $_POST['uploadId']
        ));

        $model = $s3->completeMultipartUpload(array(
            'Bucket' => TMP_IMG,
            'Key' => $_POST['key'],
            'UploadId' => $_POST['uploadId'],
            'Parts' => $partsModel['Parts']
        ));

        sendJson(array(
            'success' => true
        ));
        break;

    case 'AbortMultipartUpload':
        # code...
        break;

    default:
        # code...
        break;
}

Is there anything that is obviously wrong on how I am accomplishing my task. My only thought would be that my part size it too small for the upload. If that is the case, I do have a work around for that with another version of my project that I have been working on.

like image 738
GelPen Avatar asked May 21 '15 20:05

GelPen


People also ask

What is S3 ObjectCreated CompleteMultipartUpload?

s3:ObjectCreated:CompleteMultipartUpload – An object was created by the completion of a S3 multi-part upload. s3:ObjectCreated:* – An object was created by one of the event types listed above or by a similar object creation event added in the future.

What is CompleteMultipartUpload?

PDF. Completes a multipart upload by assembling previously uploaded parts. You first initiate the multipart upload and then upload all parts using the UploadPart operation.

When working with S3 through the API you get an error response as 409 conflict What could be the reason for this?

You are working with the S3 API and receive an error: 409 Conflict. What is a possible cause of this error? You're attempting to delete a bucket without first removing the contents in the bucket. Explanation: A 409 HTTP Status Code can indicate a BucketNotEmpty error code.


1 Answers

I just spend a few too many minutes* on this same problem, until I realized that the Parts argument should be nested in a MultipartUpload entry. My code now looks like this:

$completeParams = Array(
    'Bucket' => $multipartUpload['Bucket'],
    'Key' => $multipartUpload['Key'],
    'MultipartUpload' => Array(
        'Parts' => $parts,
    ),
    'UploadId' => $multipartUpload['UploadId']
);

$res = $client->completeMultipartUpload($completeParams);

And that works just fine. At least when using version 3 of the SDK. Version 2 of the SDK expects Parts directly in $params.

*I won't reveal how many minutes

like image 162
Wouter van Vliet Avatar answered Oct 22 '22 06:10

Wouter van Vliet