javascriptamazon-s3mechanicalturkmturk

MTurk how to upload file to S3 when task is submitted


I am trying to create a task on Amazon MTurk, where the workers would collect some data and upload a single file when they are ready & submit the task. When the task is submitted, I want to upload the file to my linked S3 bucket - which is mostly based on this tutorial.

However, the file is sometimes uploaded successfully, and sometimes not. Since the S3.upload function is asynchronous, it looks like the task submission is sometimes completed before the file upload is completed. I am a javascript newbie: I tried to make this happen synchronously, but it still doesn't work properly. Here is my javascript code:

<script>
    let config = {
        region: 'xxx',
        pool: 'xxx',
        bucket: 'xxx'
    }
    
    AWS.config.region = config.region;
    AWS.config.credentials = new AWS.CognitoIdentityCredentials({
        IdentityPoolId: config.pool,
    });
    
    var s3 = new AWS.S3({
        apiVersion: '2006-03-01',
        params: {Bucket: config.bucket},
    });
  
    start_upload = function (event) {
        $("#status").text("Uploading...");
    
        let file = $("#file").prop('files')[0];
        if (file === null || file === undefined) {
            alert("You must upload a file before submitting.");
            $("#status").text("");
            return false;
        }
    
        console.log('Filename: ' + file.name);
    
        let workerId = turkGetParam('workerId');
        let fileKey = '${food_name}' + '/' + workerId + '-' + file.name;
    
        return upload_to_s3(file, fileKey);
    };
  
    upload_to_s3 = async (file, fileKey) => {
        const params = {
            Key: fileKey,
            Body: file,
            ContentType: file.type,
            ACL: 'bucket-owner-full-control'
        };
    
        try {
            console.log("Starting upload...");
            const data = await s3.upload(params).promise();
            console.log("Done uploading file");
            $("#status").text("Success.");
            return true;
        } catch (err) {
            console.log("Error uploading data. ", err);
            alert("Failed to upload, please try again. If the problem persists, contact the Requester.");
            $("#status").text("");
            return false;
        }
    }
  
    // Validate and upload file on submit
    window.onload = function() {document.getElementById('submitButton').setAttribute('onclick', 'return start_upload()'); }
</script>

Here is the relevant part of the layout of this task (HIT): enter image description here

How can I make sure that the file upload is completed before the task is completed? I saw that I can overwrite the default submit button added by MTurk, but I would prefer not doing that if possible.


Solution

  • I've found the problem: S3#upload returns a ManagedUpload object, but it doesn't mean that the file upload is completed. I am now using promises and in the callback I submit the form manually. Note that the form is provided by MTurk by default. I just find it by its ID and invoke the submit function manually.

    For reference, here is the working code:

    <script>
        let config = {
            region: 'xxx',
            pool: 'xxx',
            bucket: 'xxx'
        }
        
        AWS.config.region = config.region;
        AWS.config.credentials = new AWS.CognitoIdentityCredentials({
            IdentityPoolId: config.pool,
        });
        
        var s3 = new AWS.S3({
            apiVersion: '2006-03-01',
            params: {Bucket: config.bucket},
        });
        
        start_upload = function (event) {
            $("#status").text("Uploading, please wait...");
        
            let file = $("#file").prop('files')[0];
            if (file === null || file === undefined) {
                alert("You must choose a file before submitting.");
                $("#status").text("");
                return false;
            }
        
            let workerId = turkGetParam('workerId');
            let fileKey = '${food_name}' + '/' + workerId + '-' + file.name;
        
            upload_to_s3(file, fileKey);
            
            return false;
        };
      
        upload_to_s3 = (file, fileKey) => {
            const params = {
                Key: fileKey,
                Body: file,
                ContentType: file.type,
                ACL: 'bucket-owner-full-control'
            };
        
            let promise = s3.upload(params).promise();
            promise.then( (data) => {
                console.log("Upload completed");
                $("#status").text("Success.");
                
                const form = document.getElementById('mturk_form');
                form.submit();
            }, (err) => {
                console.log("Upload failed!!!", err);
                alert("Failed to upload, please try again. If the problem persists, contact the Requester.");
                $("#status").text("");
            } );
        }
      
        // Validate and upload file on submit
        window.onload = function() {document.getElementById('submitButton').setAttribute('onclick', 'return start_upload()'); }
    </script>