2

I am trying to create small upload in JavaScript using Drag and Drop. I have written the code, however it does not seems to work. The way that currently the website works, the uploading script must supply website with information such as PHPSESSID, additional data for server to process the request and the file itself. In case the user drops multiple files the script should upload all of them one by one. Currently however I can not even get the JavaScript to upload one file. When looking in the debug tools, the server is responding with a website itself, rather than response which is intended for file upload.

When uploading a file using already existing form on older version of the code, I have noticed that the request from web browser before the file content was sent was looking like this:

Content-Disposition: form-data; name="file"; filename="file.txt"
Content-Type: text/plain

however when sending using the script I have used, I have seen only this:

Content-Disposition: form-data; name="file"

I am however unsure what is wrong with the code, and was not able to find anything that would be useful to solve this issue.

I would like to use no additional libraries such as JQuery or anything similar

Javascript code:

function drop(e){
    e.preventDefault();
    if(e.dataTransfer.items){
        for(var i=0; i < e.dataTransfer.items.length; i++){
            var reader = new FileReader();
            var req = new XMLHttpRequest();
            var f = e.dataTransfer.files[i]
            req.onprogress = function(e){
                if (e.lengthComputable){
                    console.log("progress: " + e.loaded / e.total);
                }
            }
            req.open("POST", document.location, true);
            req.setRequestHeader("Content-Type", "application/octet-stream");
            reader.onload = function(e){
                var fd = new FormData();
                fd.append("PHPSESSID", document.cookie.split("=")[1]);
                fd.append("request", JSON.stringify({"command":"upload", "path":path + f.name, "path-type":pathtype}))
                fd.append("file", e.target.result);
                req.send(fd);
            }
            reader.readAsBinaryString(e.dataTransfer.items[i].getAsFile());
        }
    }else{
        for(var i=0; i < e.dataTransfer.files.length; i++){
            console.log(e.dataTransfer.files[i]);
        }
    }
}

PHP code which takes care of uploading:

if(isset($_POST['request'])){
    $request = json_decode($_POST['request'], true);
    if($request['command'] == "upload"){
        if(isset($request['path']) && isset($request['path-type'])){
            $path = "";
            if($request['path-type'] === "private"){
                $path = truepath($private_dir . $request['path']);
                if(!(substr($path, 0, strlen($private_dir)) === $private_dir)){
                    die();
                }
            }elseif($request['path-type'] === "public"){
                $path = truepath($public_dir . $request['path']);
                if(!(substr($path, 0, strlen($public_dir)) === $public_dir)){
                    die();
                }
            }else{
                die();
            }
            move_uploaded_file($_FILES["file"]["tmp_name"], $path);
            die();
        }
    }
    die();
}

1 Answer 1

1

After few days of experimenting I have found out that the reason why this was failing on server side was because $_POST and $_FILES were not set by the code.

There were multiple reasons for which the code failed, all caused from JavaScript side.

Things that needed to be changed:

  1. FileReader was completely unnecessary.
  2. req.setRequestHeader("Content-Type", "application/octet-stream"); was incorrect as it should be 'multipart/form-data'. Other than that it should not be set, as it will break the content type header. This is because when uploading a file the Content-Type header is also containing boundary, which is generated by browser. When you set Content-Type you will overwrite whatever the browser generated for the file upload. I was able to find this out thanks to this answer.

The code on PHP side was (surprisingly) correct. The newest code for JavaScript is:

function drop(e){
    e.preventDefault();
    if(e.dataTransfer.files){
        for(var i=0; i < e.dataTransfer.files.length; i++){
            var req = new XMLHttpRequest();
            var f = e.dataTransfer.files[i]
            req.onprogress = function(e){
                if (e.lengthComputable){
                    console.log("progress: " + e.loaded / e.total);
                }
            }
            req.open("POST", document.location, true);
            req.overrideMimeType('application/octet-stream')
            var fd = new FormData();
            fd.append("PHPSESSID", document.cookie.split("=")[1]);
            fd.append("request", JSON.stringify({"command":"upload", "path":path + f.name, "path-type":pathtype}))
            fd.append("file", f);
            req.send(fd);
        }
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.