Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NanoHTTPD: upload a file using a html form (POST)

i'm using NanoHTTPD webserver 2.1.0 on a Java Desktop Env. (no Android)

Everything is working fine...but not the file upload using the POST method (PUT is not supported with forms)

Here is my HTML code:

<form method="post" enctype="multipart/form-data">
    choose a file<br>
    <input name="file" type="file" size="50" maxlength="100000"">
    <button type="submit">upload</button>
</form>

And here is my server methode:

public Response serve(IHTTPSession session) {
    if (session.getMethod() == Method.POST) {
        Map<String, String> files = new HashMap<String, String>();
        session.parseBody(files);
        //this prints {file=C:\path-to-java-tmp-files\NanoHTTPD-4635244586997909485}
        //the number is always different
        System.out.println(files.toString());
    } else {
        //page containing the index.html including the form
        return page;
    }
}

And here is the Problem: The temp-file doesn't exist. There is another temp-file with a different "number" at the end existing and this seems to be the correct file, because the content is the same as the content of the uploaded file. So how to get the correct temp-filename?

Another problem is: The temp-file contains the hole POST content:

-----------------------------115801144322347
Content-Disposition: form-data; name="file"; filename="filename.txt"
Content-Type: application/octet-stream

-->content of the uploaded file


-----------------------------115801144322347--

This is a problem if the content is a pic or a binary.

It seems NanoHTTPD doesn't do any spezial with a POST request. it does always the same...saving the request to a tmp-file and serving a page. So: - How to get the correct temp-file? --> I think this is a bug. I'm getting a correct path and name, but the "number" is broken. idk...should i temporarily change the java tmp-path if an upload happens and then delete the file always. Then I only have one tmp-file independent of any naming? - how to kill the html request header out of the file

Or i'm doing something wrong? Is this the correct way to upload files to nanohttpd?

thx for your help!

like image 599
user3796786 Avatar asked Jul 21 '14 14:07

user3796786


Video Answer


2 Answers

Old question! But I was looking for a solution to just this problem. I was having problems dealing with the IHTTPSession input stream. Turns out the stream would prematurely run empty when reading it, and you have to go back for more. This occurred when I was sending application/octet-stream data larger than around 500k (I didn't profile for an exact figure).

My solution is to open the IHTTPSession input stream, and iteratively read the input stream until it is FULLY exhausted:

public Response post_upload(IHTTPSession session) {   
    System.out.println("received HTTP post with upload body...");  
    int streamLength = Integer.parseInt(session.getHeaders().get("content-length"));
    System.out.println("Content length is: " + streamLength);
    byte[] fileContent = new byte[streamLength];
    try {
        InputStream input = session.getInputStream();
        int bytesRead = 0;
        int iterations = 0;
        while (bytesRead < streamLength) {              
            int thisRead = input.read(fileContent, bytesRead, streamLength-bytesRead);                
            bytesRead += thisRead;
            iterations++;
        }
        System.out.println("Read " + bytesRead + " bytes in " + iterations + " iterations.");
    }
    catch (Exception e) {
        System.out.println("We have other problems...");
    }
    return newFixedLengthResponse(Response.Status.OK, "application/json", "[\"no prob\"]");
}

I hope this helps.

like image 135
DWR Avatar answered Sep 30 '22 13:09

DWR


I figured it out not long after I posted my "did you find a fix?" question, but forgot to post it until you replied today.

<form method="post" enctype="multipart/form-data" action="http://whatever.com/path/">
    <input type="file" name="file" />
    <input type="hidden" name="extra_data" value='blah blah blah' />
    <input type="submit" value="Send" />
</form>

And the Java code:

if (session.getMethod() == Method.POST) {
    try { session.parseBody(files); }
    catch (IOException e1) { e1.printStackTrace(); }
    catch (ResponseException e1) { e1.printStackTrace(); }

    extraData = session.getParms().get("extra_data");
    File file = new File(files.get("file"));
}

The important thing is that the data you're sending comes with file data, filename and file mime type.

In my case, I was using python request to POST and wasn't sending it sufficient data. The correct post structure would be:

file_data = { 'file': ('test.png', open('../some_file.png', 'rb'), 'image/png') }
requests.post("http://whatever.com/path", data = { 'extra_data': "blah blah blah" }, files = file_data)

The code above reads from "some_file.png", but tells the server it's called "test.png".

Hope that helps!

like image 31
twig Avatar answered Sep 30 '22 13:09

twig