2

I am experiencing difficulties with uploading files via ajax without sing html form. My case is like this:

  1. I have a textarea
  2. I would like to zip the content of that textarea and upload it to the server via AJAX (right now I am using JSZip)
  3. For testing purpose, I try to create a dummy zip file to send like this (not getting textarea content):

    var zip = new JSZip();
    zip.file("hello1.txt", "Hello First World\n");<br/>
    zip.file("hello2.txt", "Hello Second World\n");<br/>
    var content = zip.generate();
    
  4. Then I use Jquery ajax method to send, like this:

    $.post("the_url",
        {
            GradeRequest : {
                submitter_id : "foobar",
                evaluationset_id : 9,
                mode : 1,
                source_file : "a.cpp",
                file: content
            }
        }
    );
    

But the file is not received on the server. I have read question in How can I upload files asynchronously?, but all solutions are using html form. Is there any solution which does not involve any html form ?

Thanks in advance.

In the server side, I'm using Yii PHP Framework:

$model = $this->model;
    if ($model !== null && isset($_POST['GradeRequest'])) {
        $model->setAttributes($_POST['GradeRequest']);

        if ($model->validate()) {
            if (isset($_FILES['GradeRequest']['tmp_name']['file']) && $_FILES['GradeRequest']['tmp_name']['file'] !== "") {
                $model->file = file_get_contents($_FILES['GradeRequest']['tmp_name']['file']);
                $model->source_file = $_FILES['GradeRequest']['name']['file'];
            }

            $this->setReply(true, "Ok");
            $model->client_id = $this->clientId;
            $model->request_id = $this->requestRecord->id;
            $model->save();
        } else {
            $this->setReply(false, $model->getErrors());
        }
    }

As a proof of concept, I have created a form version like this and it works (the file got uploaded to the server):

<form enctype="multipart/form-data" method="post" action="[the_url]>
EvaluationSet id: <input type="text" name="GradeRequest[evaluationset_id]" /><br />
<input type="hidden" name="GradeRequest[mode]" value="0" />
<input type="hidden" name="GradeRequest[submitter_id]" value="Someone" />

Source file : <input type="text" name="GradeRequest[source_file]" /><br />
File : <input type="file" name="GradeRequest[file]" /><br />

<input type="submit" />

2
  • Please post the code you're running on the server. You should be able to access the file from the POST array. Commented Jun 17, 2012 at 9:31
  • @JamWaffles: code added. Commented Jun 17, 2012 at 9:51

2 Answers 2

3

There the FormData object that can help you (it's part of the so called xmlHttpRequest version 2): this is a compatibility chart, with some linked references and examples.

With that, you can add key/value pairs to the POST form, including File objects, and send it all via the common send method of xmlHttpRequest.

File objects can be easily retrieved using a <input type="file"> element, or even using drag & drop from your desktop.

If you want to upload some file content as a file, you'll have to create a Blob. This feature is still experimental but supported by Chrome and Firefox (at least... and Safari I guess?):

var builder = new BlobBuilder();
builder.append(content);
var blob = builder.getBlob("application/zip");

Keep in mind that at this moment you'll have to use MozBlobBuilder in Firefox and WebKitBlobBuilder in Chrome instead.

In some tutorials I've seen that the string is actually converted into a Uint8Array first. Maybe that's based on a older reference, because the append method of BlobBuilder should accept plain strings too. Never tried it though.

If your content is a Base64 encoded string, you'll have to convert it using atob (should be supported by every browser that also supports Blob and FormData).

Edit: BlobBuilder is now deprecated due to the new draft of the Blob constructor. So everything you'll have to do to get the Blob is:

var blob = new Blob([content], "application/zip");

The rest is quite simple:

var form = new FormData();
form.append("file", blob);

The problem here is that the file name on the server side is unpredictable and depends on the user agent. I've seen some uses of append with a third parameter specifying the file name, but I guess it's a good idea to send the actual file name to a separate key/value pair in the FormData object.

Sign up to request clarification or add additional context in comments.

7 Comments

for the compatibility chart, I don't really understand about it. Could you please explain more ? Yes, I am aware with the easy usage of <input type="file">, but the problem is: I wanted to send file from the text entered by the user, not from existing file in computers.
You'll still need FormData, but you can begin explaining how JSZip works. Does it create a file? And where? Locally? On a server?
In the JSZip example, they use: var content = zip.generate(); location.href="data:application/zip;base64,"+content; I think the 'created' file is in the content variable. There is no real file created in the client computer.
So all you have is a base64 string. This is a little bit complex, since if you have to send a file, and not a string, you'll have to create one first. The only browser that allows you to do that at the moment, supporting a decently complete FileAPI library, is Google Chrome.
oouucchh....so it's currently not possible to fake the file-creation and send it via XHR. Do you think I should send the textarea content as a plain string ? Would that be a good workaround ?
|
1

I finally get it through !! Many thanks for MaxArt.

Basically, it is what MaxArt has said with Uint8Array conversion. I got the conversion reference from Eric Bidelman here.

Here's how I've done it:

var zip = new JSZip();
zip.file("hello1.txt", "Hello First World\n");
zip.file("hello2.txt", "Hello Second World\n");
var content = zip.generate(); //Generate dummy zip file (adjust to your need)

var oBlob = new (window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder)(); //Instantiate blob builder

var raw = atob(content);    //decode the base64 string
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) { //convert to uInt8Array
    uInt8Array[i] = raw.charCodeAt(i);
}

oBlob.append(uInt8Array.buffer); //append it to blobbuilder
oMyForm.append("GradeRequest[file]", oBlob.getBlob("application/zip")); //because you create a zip file, so get the zip type

//send it
var oReq = new XMLHttpRequest();
oReq.open("POST", "{{the_url}}");
oReq.send(oMyForm);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.