NanoHTTPD How to save uploaded file to sdcard folder

烈酒焚心 提交于 2019-12-06 13:24:12

If you are using NanoHTTPD r.2.1.0, please try these codes:

@Override
public Response serve(IHTTPSession session) {
    Map<String, String> headers = session.getHeaders();
    Map<String, String> parms = session.getParms();
    Method method = session.getMethod();
    String uri = session.getUri();
    Map<String, String> files = new HashMap<>();

    if (Method.POST.equals(method) || Method.PUT.equals(method)) {
        try {
            session.parseBody(files);
        } catch (IOException ioe) {
            return getResponse("Internal Error IO Exception: " + ioe.getMessage());
        } catch (ResponseException re) {
            return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
        }
    }

    if ("/uploadfile".equalsIgnoreCase(uri)) {
        String filename = parms.get("filename");
        String tmpFilePath = files.get("filename");
        if (null == filename || null == tmpFilePath) {
            // Response for invalid parameters
        }
        File dst = new File(mCurrentDir, filename);
        if (dst.exists()) {
            // Response for confirm to overwrite
        }
        File src = new File(tmpFilePath);
        try {
            InputStream in = new FileInputStream(src);
            OutputStream out = new FileOutputStream(dst);
            byte[] buf = new byte[65536];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
        } catch (IOException ioe) {
            // Response for failed
        }
        // Response for success
    }

    // Others...
}

Here's my working code:

public Response serve(IHTTPSession session) {
    Map<String, String> headers = session.getHeaders();
    Map<String, String> parms = session.getParms();
    Method method = session.getMethod();
    String uri = session.getUri();
    Map<String, String> files = new HashMap<>();

    if (Method.POST.equals(method) || Method.PUT.equals(method)) {
        try {
            session.parseBody(files);
        } catch (IOException ioe) {
            return getResponse("Internal Error IO Exception: " + ioe.getMessage());
        } catch (ResponseException re) {
            return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
        }
    }

    uri = uri.trim().replace(File.separatorChar, '/');
    if (uri.indexOf('?') >= 0) {
        uri = uri.substring(0, uri.indexOf('?'));
    }

    // Other implementation goes here...

    if ("/uploadfiles".equalsIgnoreCase(uri)) {
        String filename, tmpFilePath;
        File src, dst;
        for (Map.Entry entry : parms.entrySet()) {
            if (entry.getKey().toString().substring(0, 8).equalsIgnoreCase("filename")) {
                filename = entry.getValue().toString();
                tmpFilePath = files.get(entry.getKey().toString());
                dst = new File(mCurrentDir, filename);
                if (dst.exists()) {
                    return getResponse("Internal Error: File already exist");
                }
                src = new File(tmpFilePath);
                if (! copyFile(src, dst)) {
                    return getResponse("Internal Error: Uploading failed");
                }
            }
        }
        return getResponse("Success");
    }

    return getResponse("Error 404: File not found");
}

private boolean deleteFile(File target) {
    if (target.isDirectory()) {
        for (File child : target.listFiles()) {
            if (! deleteFile(child)) {
                return false;
            }
        }
    }
    return target.delete();
}

private boolean copyFile(File source, File target) {
    if (source.isDirectory()) {
        if (! target.exists()) {
            if (! target.mkdir()) {
                return false;
            }
        }
        String[] children = source.list();
        for (int i = 0; i < source.listFiles().length; i++) {
            if (! copyFile(new File(source, children[i]), new File(target, children[i]))) {
                return false;
            }
        }
    } else {
        try {
            InputStream in = new FileInputStream(source);
            OutputStream out = new FileOutputStream(target);

            byte[] buf = new byte[65536];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
            in.close();
            out.close();
        } catch (IOException ioe) {
            return false;
        }
    }
    return true;
}

private Response getResponse(String message) {
    return createResponse(Response.Status.OK, MIME_PLAINTEXT, message);
}

// Announce that the file server accepts partial content requests
private Response createResponse(Response.Status status, String mimeType, String message) {
    Response res = new Response(status, mimeType, message);
    res.addHeader("Accept-Ranges", "bytes");
    return res;
}

In order to upload multiple files in a single input file like:

<input type="file" name="filename" multiple>

I modify decodeMultipartData() method in NanoHTTPD.java from:

        private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
                                     Map<String, String> files) throws ResponseException {
        try {
            int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
            int boundarycount = 1;
            String mpline = in.readLine();
            while (mpline != null) {
                if (!mpline.contains(boundary)) {
                    throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
                }
                boundarycount++;
                Map<String, String> item = new HashMap<String, String>();
                mpline = in.readLine();
                while (mpline != null && mpline.trim().length() > 0) {
                    int p = mpline.indexOf(':');
                    if (p != -1) {
                        item.put(mpline.substring(0, p).trim().toLowerCase(Locale.US), mpline.substring(p + 1).trim());
                    }
                    mpline = in.readLine();
                }
                if (mpline != null) {
                    String contentDisposition = item.get("content-disposition");
                    if (contentDisposition == null) {
                        throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
                    }
                    StringTokenizer st = new StringTokenizer(contentDisposition, ";");
                    Map<String, String> disposition = new HashMap<String, String>();
                    while (st.hasMoreTokens()) {
                        String token = st.nextToken().trim();
                        int p = token.indexOf('=');
                        if (p != -1) {
                            disposition.put(token.substring(0, p).trim().toLowerCase(Locale.US), token.substring(p + 1).trim());
                        }
                    }
                    String pname = disposition.get("name");
                    pname = pname.substring(1, pname.length() - 1);

                    String value = "";
                    if (item.get("content-type") == null) {
                        while (mpline != null && !mpline.contains(boundary)) {
                            mpline = in.readLine();
                            if (mpline != null) {
                                int d = mpline.indexOf(boundary);
                                if (d == -1) {
                                    value += mpline;
                                } else {
                                    value += mpline.substring(0, d - 2);
                                }
                            }
                        }
                    } else {
                        if (boundarycount > bpositions.length) {
                            throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request");
                        }
                        int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
                        String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
                        files.put(pname, path);
                        value = disposition.get("filename");
                        value = value.substring(1, value.length() - 1);
                        do {
                            mpline = in.readLine();
                        } while (mpline != null && !mpline.contains(boundary));
                    }
                    parms.put(pname, value);
                }
            }
        } catch (IOException ioe) {
            throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
        }
    }

tobe:

        private void decodeMultipartData(String boundary, ByteBuffer fbuf, BufferedReader in, Map<String, String> parms,
                                     Map<String, String> files) throws ResponseException {
        try {
            String pname_0 = "";
            String pname_1 = "";
            int pcount = 1;

            int[] bpositions = getBoundaryPositions(fbuf, boundary.getBytes());
            int boundarycount = 1;
            String mpline = in.readLine();
            while (mpline != null) {
                if (!mpline.contains(boundary)) {
                    throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
                }
                boundarycount++;
                Map<String, String> item = new HashMap<String, String>();
                mpline = in.readLine();
                while (mpline != null && mpline.trim().length() > 0) {
                    int p = mpline.indexOf(':');
                    if (p != -1) {
                        item.put(mpline.substring(0, p).trim().toLowerCase(Locale.US), mpline.substring(p + 1).trim());
                    }
                    mpline = in.readLine();
                }
                if (mpline != null) {
                    String contentDisposition = item.get("content-disposition");
                    if (contentDisposition == null) {
                        throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
                    }
                    StringTokenizer st = new StringTokenizer(contentDisposition, ";");
                    Map<String, String> disposition = new HashMap<String, String>();
                    while (st.hasMoreTokens()) {
                        String token = st.nextToken().trim();
                        int p = token.indexOf('=');
                        if (p != -1) {
                            disposition.put(token.substring(0, p).trim().toLowerCase(Locale.US), token.substring(p + 1).trim());
                        }
                    }
                    String pname = disposition.get("name");
                    pname = pname.substring(1, pname.length() - 1);

                    if (pname.contentEquals(pname_0)) {
                        pname_1 = pname + String.valueOf(pcount);
                        pcount++;
                    } else {
                        pname_0 = pname;
                        pname_1 = pname;
                    }

                    String value = "";
                    if (item.get("content-type") == null) {
                        while (mpline != null && !mpline.contains(boundary)) {
                            mpline = in.readLine();
                            if (mpline != null) {
                                int d = mpline.indexOf(boundary);
                                if (d == -1) {
                                    value += mpline;
                                } else {
                                    value += mpline.substring(0, d - 2);
                                }
                            }
                        }
                    } else {
                        if (boundarycount > bpositions.length) {
                            throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request");
                        }
                        int offset = stripMultipartHeaders(fbuf, bpositions[boundarycount - 2]);
                        String path = saveTmpFile(fbuf, offset, bpositions[boundarycount - 1] - offset - 4);
                        files.put(pname_1, path);
                        value = disposition.get("filename");
                        value = value.substring(1, value.length() - 1);
                        do {
                            mpline = in.readLine();
                        } while (mpline != null && !mpline.contains(boundary));
                    }
                    parms.put(pname_1, value);
                }
            }
        } catch (IOException ioe) {
            throw new ResponseException(Response.Status.INTERNAL_ERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage(), ioe);
        }
    }

Hope this help and sorry for my bad English..:-)

To allow multiple file upload:

<input type="file" name="filename" multiple>

The same issue existed in the 2.2.1 branch. Following the same logic, I fixed the same function with a few lines of code change.

Add a counter pcount at the beginning of the function:

private void decodeMultipartFormData(String boundary, String encoding, ByteBuffer fbuf, Map<String, String> parms, Map<String, String> files) throws ResponseException {
   int pcount = 1;
   try {

Then use the counter to update the keyname if filename is not empty:

while (matcher.find()) {
     String key = matcher.group(1);
     if ("name".equalsIgnoreCase(key)) {
         part_name = matcher.group(2);
     } else if ("filename".equalsIgnoreCase(key)) {
         file_name = matcher.group(2);
         // add these two line to support multiple
         // files uploaded using the same field Id
         if (!file_name.isEmpty()) {
            if (pcount > 0)
               part_name = part_name + String.valueOf(pcount++);
            else
               pcount++;
         }
     }
}

Maybe late, but just for latecommers just like me.

Explained before, the client use okhttp upload a file just like the follow code:

RequestBody requestBody = new MultipartBody.Builder()
            .setType(MultipartBody.FORM)
    //sourceFile is a File as you know
            .addFormDataPart("image_file_1", "logo-square1.png", RequestBody.create(MediaType.parse("image/png"), sourceFile))
            .build();

    Request request = new Request.Builder()
            .url(url)
            .post(requestBody)
            .build();
    Response response = client.newCall(request).execute();

The follow code is what you want

@Override
public Response serve(IHTTPSession session) {

    Method method = session.getMethod();
    // ▼ 1、parse post body ▼
    Map<String, String> files = new HashMap<>();
    if (Method.POST.equals(method) || Method.PUT.equals(method)) {
        try {
            session.parseBody(files);
        } catch (IOException ioe) {
            return getResponse("Internal Error IO Exception: " + ioe.getMessage());
        } catch (ResponseException re) {
            return newFixedLengthResponse(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
        }
    }
    //after the body parsed, by default nanoHTTPD will save the file to cache and put it into params( "image_file_1" as key and the value is "logo-square1.png");
    //files key is just like "image_file_1", and the value is nanoHTTPD's template file path in cache
    // ▲ 1、parse post body ▲


    // ▼ 2、copy file to target path xiaoyee ▼
    Map<String, String> params = session.getParms();
    for (Map.Entry<String, String> entry : params.entrySet()) {
        final String paramsKey = entry.getKey();
        if (paramsKey.contains("image_file_1")) {
            final String tmpFilePath = files.get(paramsKey);
            final String fileName = paramsKey;
            final File tmpFile = new File(tmpFilePath);
            final File targetFile = new File(mCurrentDir + fileName);
            LogUtil.log("copy file now, source file path: %s,target file path:%s", tmpFile.getAbsoluteFile(), targetFile.getAbsoluteFile());
            //a copy file method just what you like
            copyFile(tmpFile, targetFile);

            //maybe you should put the follow code out
            return getResponse("Success");
        }
    }
    // ▲ 2、copy file to target path xiaoyee ▲

    return getResponse("Error 404: File not found");
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!