0

I have been working on a browser based file uploader that sends files to our S3 buckets for the past couple of months. I've made great progress in the last couple of weeks. However, my biggest road block came in the form of iDevices. Pretty much anything that runs on iOS 8 has been giving me major headaches relating to my uploader. I am able to run my program on Chrome, Firefox, and Safari on desktops/laptops on both PC and MAC. However, when it comes to mobile devices, only Android devices work the intended way. On iOS, Chrome only uploads small files and anything large will return a file that has a size of 0. Safari out right fails. I know that there have been issues with iOS 8 relating to this issue, but from my research, this issue should have been taken care of already. Anyways, I was wondering if there was a work around this problem so that I can upload my files like I am doing for the other devices and browsers.

Error uploading a small image file:

Syntax: JSON Parse error: Unexpected EOF

Failed to load resource: request body stream exhausted

Error uploading a big video file:

Failed to load resource: request body stream exhausted

SyntaxError: JSON Parse error: Unrecognized token '<'

If you would like to see any examples of my code, feel free to ask as I will edit my post for this request.

upload.js

    var files;                      // Array of inputed files
    var ownerName;                  // Inputed name of file owner
    var totalSize = 0;              // Total file size of all files
    var loaded    = 0;              // Loaded data amount
    var MB        = 1024 * 1024;    // 1 MB
    var partSize  = MB;             // Data amount of part of chunk of file 
    var chunkSize = 5 * MB;         // Data amount of chunk of file

    function request(command, xhr, formdata){
        xhr.open("POST", "FileUploader.php", true);
        if(command === 'UploadPart' || command === 'PutObject')
            xhr.upload.addEventListener("progress", progressHandler, false);
        xhr.addEventListener("load", completeHandler, false);
        xhr.addEventListener("error", errorHandler, false);
        xhr.addEventListener("abort", abortHandler, false);
        xhr.send(formdata);
        return xhr;
    }

    function uploadFile(){
        reset();
        if (window.File && window.FileReader && window.FileList && window.Blob){
            files = _("file").files;
            totalSize = calcTotalSize(files);
            for (var i = 0; i < files.length; i++){
                var key = createKey(_("name").value, files[i]);
                if (files[i].size >= chunkSize)
                    createMultipartUpload(i, key);  
                else
                    putObject(i, key);
            }
        }
        else
            alert("Your browser does not support our File Uploader.");
    }

    function createMultipartUpload(i, key){
        var formdata = new FormData();
        formdata.append("command", 'CreateMultipartUpload');
        formdata.append("fileindex", i);
        formdata.append("key", key);
        var xhr = request('CreateMultipartUpload', new XMLHttpRequest(), formdata);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4)
                uploadPart(JSON.parse(xhr.responseText), 1);
        };
    }

    function uploadPart(sendBackData, partNum){
        console.log("Uploading part " + partNum); 
        var fileIndex = sendBackData['fileindex'];
        var file      = files[fileIndex];

        if (partNum > Math.ceil(file.size / chunkSize)) {
            completeMultipartUpload(sendBackData);
            return;
        }

        var blobs     = [];
        var start     = (partNum - 1) * chunkSize;
        var end       = start + partSize;
        var index     = 0;

        while (start < chunkSize*partNum){
            blobs.push(file.slice(start, end));
            start = end;
            end = start + partSize;
            index++;
        }

        var formdata = new FormData();
        for(var i = 0; i < index; i++)
            formdata.append("file[]", blobs[i]);
        formdata.append("command", 'UploadPart');
        formdata.append("uploadId", sendBackData['uploadId']);
        formdata.append("key", sendBackData['key']);
        formdata.append("partNumber", partNum);

        var xhr = request('UploadPart', new XMLHttpRequest(), formdata);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4)
                uploadPart(sendBackData, partNum + 1);
        };
    }

    function completeMultipartUpload(sendBackData) {
        var formdata = new FormData();
        formdata.append("command", 'CompleteMultipartUpload');
        formdata.append("uploadId", sendBackData['uploadId']);
        formdata.append("key", sendBackData['key']);
        
        var xhr = request('CompleteMultipartUpload', new XMLHttpRequest(), formdata);
        xhr.onreadystatechange = function() {
            if (xhr.readyState === 4) {
                var sendBackData = JSON.parse(xhr.responseText);
                if(sendBackData['success'])
                    displayURL(sendBackData['url']);
            }
        };
    }

FileUploader.php

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

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

    $command = $_POST['command'];
    s3Command($command);

function s3Command($command){
    $key = $_POST['key'];
    switch ($command) {
        case 'CreateMultipartUpload':
            $fileindex = $_POST['fileindex'];
            
            $response = $GLOBALS["s3"]->createMultipartUpload(array(
                'Bucket'    => TMP_IMG,
                'Key'       => $key,
                'ACL'       => 'public-read',
            ));

            $uploadId = $response['UploadId'];

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

        case 'UploadPart':
            $tmp_files = $_FILES['file'];
            $files = array();
            for ($i = 0; $i < count($tmp_files['name']); $i++){
                $files[] = array(
                    'name' => $tmp_files['name'][$i],
                    'tmp_name' => $tmp_files['tmp_name'][$i],
                    'type' => $tmp_files['type'][$i],
                    'size' => $tmp_files['size'][$i],
                    'error' => $tmp_files['error'][$i]
                );
            }
            
            $body = mergeFiles($files);

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

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

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

            $url = $GLOBALS["s3"]->getObjectUrl(TMP_IMG, $key);
            
            $type = explode('/', $key);

            if($type[0] == 'image'){
                $result = $GLOBALS["s3"]->getObject(array(
                    'Bucket' => TMP_IMG,
                    'Key'    => $key
                ));

                $body = $result['Body'];

                $url = resizeImage($url);
            }

            sendJson(array(
                'success' => true,
                'url'     => $url
            ));
            break;

        case 'AbortMultipartUpload':
            $s3->abortMultipartUpload(array(
                'Bucket'   => TMP_IMG,
                'Key'      => $key,
                'UploadId' => $_POST['uploadId']
            ));
            break;
    }
}
?>
Community
  • 1
  • 1
GelPen
  • 141
  • 1
  • 9
  • Would generally suggest making use of JQuery Mobile. To better answer your question, need to kow more about the issue. Does the upload happen at all? Any log information? HTTP Errors? – Twisty Jun 03 '15 at 16:43
  • I added in what errors I was getting from uploading on Safari iOS. The common errors between the two are JSON Parsing errors and failing to load the resource because stream was exhausted. – GelPen Jun 03 '15 at 17:50
  • This could mean a few things. Would need to see your code that you're using. – Twisty Jun 04 '15 at 15:41
  • I added in my code for what I did. I didn't add any of my code that wasn't necessary to look at. – GelPen Jun 04 '15 at 16:25
  • Do you encounter these errors all the time or only when chunking a file? – Twisty Jun 04 '15 at 16:32
  • All the time, but only on iOS Safari. I see the error on iOS Chrome only when upload a video file or similarly large file. – GelPen Jun 04 '15 at 16:40
  • Sounds like Safari is doing something weird with the slicing. Unexpected End of File suggests something went 1 step over in a loop maybe. The syntax looks correct, did you run your code through a verifier? – Twisty Jun 04 '15 at 16:43
  • No I haven't. I typically run the code on my browser and debug it using Firebug. Do you happen to have a link to a good one? – GelPen Jun 04 '15 at 16:48
  • I created a jsFiddle and used it's jsHint. It suggested switching to dot notation for a few things. So your code is valid, but to make it more valid, you can see it here: http://jsfiddle.net/Twisty/tppxp3z3/ Examples: `var fileIndex = sendBackData['fileindex']` to `var fileIndex = sendBackData.fileindex;` – Twisty Jun 04 '15 at 16:51

2 Answers2

0

The error that I should have been focusing on was the:

Failed to load resource: request body stream exhausted

I came across this page.

My issue wasn't with the JSON Parsing but the directory that my scripts were in requiring authentication on our IIS 8 server. iOS has a weird policy when it come to authentication. What I did was moved my script outside the directory and it works as intended.

Community
  • 1
  • 1
GelPen
  • 141
  • 1
  • 9
0

its working me

<form action="" method="post" enctype="multipart/form-data">
       <input type="file" name="txt_pro_img1" />
       <input type ="hidden" name="partyGroupId" id="partyGroupId" value="10050"/>
       <input type="submit" name="submit" value="Upload"/>
     </form>

<?php //ignore this comment >
    if(!empty($_POST['submit'])){
        $mob = '88888';
        $uploadFile =  "./us-admin/uploaded/" . basename('profile1'.$mob.''.date('Ymdhis').$_FILES["txt_pro_img1"]["name"]);
        $uploadFile = str_replace(' ','',$uploadFile); 
        $pro1 = 'profile1'.$mob.''.date('Ymdhis').$_FILES["txt_pro_img1"]["name"];
        $pro1 = str_replace(' ','',$pro1); 
        //$filename1 = basename('profile1'.$mob.''.date('Ymdhis').".".$extension);
        if(!(getimagesize($_FILES["txt_pro_img1"]["tmp_name"]) !== false)) {
            echo "Sorry, your image is invalid";
            exit;
        }

        $imageFileType = strtolower(pathinfo($uploadFile, PATHINFO_EXTENSION));
        if($imageFileType != "jpg"
                && $imageFileType != "png"
                && $imageFileType != "jpeg"
                && $imageFileType != "gif" ) {
            echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
            exit;
        }

        if ($_FILES["txt_pro_img1"]["size"] > 2000000) {
            echo "Sorry, your file is too large.";
            exit;
        }
        move_uploaded_file($_FILES["txt_pro_img1"]["tmp_name"], $uploadFile);
        echo $pro1." in Successfully";
    }
?>
Pankaj Upadhyay
  • 2,114
  • 21
  • 22