Spark: File upload support

Created on 19 Nov 2012  路  17Comments  路  Source: perwendel/spark

Hi, thank you for your wonderful work. Is there any way to handle files being uploaded via POST?

Most helpful comment

"org.eclipse.jetty.multipartConfig" does not work when the spark-java application is converted to web app war file deployed in Tomcat.

All 17 comments

I was using multipart request to post files to the server. To dump all uploaded files to the "upload" directory, I used Solution A at http://stackoverflow.com/questions/3337056/convenient-way-to-parse-incoming-multipart-form-data-parameters-in-a-servlet (the O'Reilly COS multipart parser), as following:

    post(new Route("/upload") {

        @Override
        public String h(Request request, Response response) throws Exception {
            final File upload = new File("upload");
            if (!upload.exists() && !upload.mkdirs()) {
                throw new RuntimeException("Failed to create directory " + upload.getAbsolutePath());
            }
            // this dumps all files contained in the multipart request to target directory.
            final MultipartRequest req = new MultipartRequest(request.raw(), upload.getAbsolutePath());
            halt(200);
            return null;
        }
    });

Thanks a ton, I ended up doing the same thing (thanks to your issue already opened) :-)
:+1: for upload-related yak shaving!

Apache commons-fileupload comes to the rescue! This example was written using spark 2.1

post("/upload", (req, res) -> {
    final File upload = new File("upload");
    if (!upload.exists() && !upload.mkdirs()) {
        throw new RuntimeException("Failed to create directory " + upload.getAbsolutePath());
    }

    // apache commons-fileupload to handle file upload
    DiskFileItemFactory factory = new DiskFileItemFactory();
    factory.setRepository(upload);
    ServletFileUpload fileUpload = new ServletFileUpload(factory);
    List<FileItem> items = fileUpload.parseRequest(req.raw());

    // image is the field name that we want to save
    FileItem item = items.stream()
                    .filter(e -> "image".equals(e.getFieldName()))
                    .findFirst().get();
    String fileName = item.getName();
    item.write(new File(dir, fileName));
    halt(200);
    return null;
});

@shauvik thanks! That worked great for me.

With spark 2.2 you could just do

if (req.raw().getAttribute("org.eclipse.multipartConfig") == null) {
 MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
 req.raw().setAttribute("org.eclipse.multipartConfig", multipartConfigElement);
}

Part part = req.raw().getPart(name);

With spark 2.3 it upgraded the jetty dependency so its now:

if (req.raw().getAttribute("org.eclipse.jetty.multipartConfig") == null) {
 MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
 req.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
}

Part part = req.raw().getPart(name);

Once you have a Part you can access its input stream to get the bytes uploaded.

Closing this, but will create a new issue about adding uploads to docs.

hello! I want to process both forminput and multipart simultaneously how can I do it?
while using multipart/form-data request.queryParams is not working...

Yes that's true, queryParams will not work in case of multipart/form-data.
_Solution_: Actually you can get all the form data using the same idiom req.raw().getPart(<form-element-name>).
Let me illustrate this with an example:
I had the following form in HTML:

<form method="POST" enctype="multipart/form-data" action="/upload">
        File to upload: <input type="file" name="file"><br />
        Name: <input type="text" name="name"><br /><br />
        <input type="submit" value="Upload"> Press here to upload the file!
</form>

I could get the input file using the method suggested by @briannesbitt , but I was not sure how to get the text-input name.
But that is also encoded as a part, and can be retrieved using req.raw().getPart("name"). This will return a Part, from which you can get the input-stream using getInputStream(), and then get the String out of this stream by whatever method you like (though Apache Common's IOUtils provides a very convenient method IOUtils.toString to get string from an input-stream).
The entire code that I used to process the form is thus:

Spark.post("/upload",(req,res)->{
    if (req.raw().getAttribute("org.eclipse.jetty.multipartConfig") == null) {
        MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
        req.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
    }
    Part file = req.raw().getPart("file");
    Part name = req.raw().getPart("name");
    String filename = file.getSubmittedFileName();
    if(name.getSize() > 0){
        try{
            filename = IOUtils.toString(name.getInputStream(), StandardCharsets.UTF_8);
        } catch(Exception e){
            e.printStackTrace();
        }
    }
    Path filePath = Paths.get(".",filename);
    Files.copy(file.getInputStream(),filePath);
    return "Done!";
});

Hope this helped!

Hi all,
I am using like this but still it gives me
javax.servlet.ServletException: Content-Type != multipart/form-data

 post("/api/:username/videos/new", (req, res) ->{
 if (req.raw().getAttribute("org.eclipse.jetty.multipartConfig") == null) {

                //MultipartConfigElement multipartConfigElement = new MultipartConfigElement("/tmp");
MultipartConfigElement multipartConfigElement = new MultipartConfigElement(System.getProperty("java.io.tmpdir"));
                req.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
            }
            String username = req.params(":username");

            //InputStream inputStream = req.raw().getPart("file").getInputStream();
            Part uploadFile = req.raw().getPart("file");
  });

are you using enctype="multipart/form-data" in the form HTML?

thanks to all for opening this wonderful module and to all commentator.
but still I have one more problem based on this topic ....please help me..

here is the code and comment in code explain the problem...thanks in advance.

public class controller{
/* constructor
connect to Db */
abstract class FreemarkerBasedRoute extends Route {
final Template template;

        protected FreemarkerBasedRoute(final String path, final String templateName) throws IOException {
            super(path);
            template = cfg.getTemplate(templateName);
        }
    .
    .
main(){

    get(new FreemarkerBasedRoute("/userHome/:name", "home.ftl") {
            @Override
            protected void doHandle(Request request, Response response, Writer writer) throws IOException, TemplateException {
                SimpleHash root = new SimpleHash();                
                String name = request.params(":name");  // problem is here**

                System.out.println("name:"+name);
                   root.put("name", name);
                                template.process(root, writer);
            }
        });
    }
       }

a href="/userHome/biren" >open /a // after clicking this link control goes to home.ftl

home.ftl

.
.
     < img  src="img.jpg"  height="400" width="400" />
/html

console output:
name:biren //yup this is actual param*
name:img.jpg //but what is this?? src part of img tag!.
*

@coderkd10

nope i'm using postman to test as of now, front-end is still in progress

@vickey290 you some how need to ensure that the form data is encoded as multi part to able to use ^ req.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement) and the exception javax.servlet.ServletException: Content-Type != multipart/form-data basically says the same.

Hello, I am trying to upload file using spark 2.2, java 8. Using the next Code:
https://groups.google.com/forum/#!msg/sparkjava/fjO64BP1UQw/CsxdNVz7qrAJ

post("/upload", "multipart/form-data", (request, response) -> {
    //- Servlet 3.x config
    String location = "/aaa/bbb";  // the directory location where files will be stored
    long maxFileSize = 100000000;  // the maximum size allowed for uploaded files
    long maxRequestSize = 100000000;  // the maximum size allowed for multipart/form-data requests
    int fileSizeThreshold = 1024;  // the size threshold after which files will be written to disk
    MultipartConfigElement multipartConfigElement = new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold);
    request.raw().setAttribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
    //-/

    Collection<Part> parts = request.raw().getParts();
    for(Part part : parts) {
        System.out.println("Name:");
        System.out.println(part.getName());
        System.out.println("Size: ");
        System.out.println(part.getSize());
        System.out.println("Filename:");
        System.out.println(part.getSubmittedFileName());
    }

    String fName = request.raw().getPart("upfile").getSubmittedFileName();
    System.out.println("Title: "+request.raw().getParameter("title"));
    System.out.println("File: "+fName);

    Part uploadedFile = request.raw().getPart("upfile");
    Path out = Paths.get("/aaa/bbb/"+fName);
    try (final InputStream in = uploadedFile.getInputStream()) {
        Files.copy(in, out);
        uploadedFile.delete();
    }
    // cleanup
    multipartConfigElement = null;
    parts = null;
    uploadedFile = null;

    return "OK";
}); 

I have the next problems:

  1. The interface part of servlet does not recognized the method getSubmittedFileName() , it generate the compile error can not find symbol.
  2. I delete the lines where this method is used, but when I tried to use post, the log show me:
    IllegalStateException: No multipart config for servlet

I have no Idea what happen, please help me

thanks friends, excuse me if my english is not the best.

"org.eclipse.jetty.multipartConfig" does not work when the spark-java application is converted to web app war file deployed in Tomcat.

@louiedecastro could you please explain your Tomcat issue? Maybe an error log and/or a code snippet from where your are using the request attribute.
I'm currently deploying an war file on Tomcat and uploading files to my application (using Spark 2.6.0) and everything is working fine here.

I'm having a similar problem. I'm also using Tomcat!
This is my code

if (req.attribute("org.eclipse.jetty.multipartConfig") == null) {
    final MultipartConfigElement multipartConfigElement = new MultipartConfigElement(FOTO_LOC);
    req.attribute("org.eclipse.jetty.multipartConfig", multipartConfigElement);
}

try {
    Collection<Part> parts = req.raw().getParts();
    for(Part part : parts) {
        System.out.println("Name:");
        System.out.println(part.getName());
        System.out.println("Size: ");
        System.out.println(part.getSize());
        System.out.println("Filename:");
        System.out.println(part.getSubmittedFileName());
    }
} catch (Exception ex) {
    ex.printStackTrace();
}

And this is what it outputs when I try to post a file:

java.lang.IllegalStateException: Unable to process parts as no multi-part configuration has been provided
    at org.apache.catalina.connector.Request.parseParts(Request.java:2788)
    at org.apache.catalina.connector.Request.getParts(Request.java:2754)
    at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1098)
    at javax.servlet.http.HttpServletRequestWrapper.getParts(HttpServletRequestWrapper.java:356)
    at com.ciarama.integrator.routes.ComentarioRoutes.lambda$0(ComentarioRoutes.java:22)
    at spark.RouteImpl$1.handle(RouteImpl.java:72)
    at spark.http.matching.Routes.execute(Routes.java:61)
    at spark.http.matching.MatcherFilter.doFilter(MatcherFilter.java:134)
    at spark.servlet.SparkFilter.doFilter(SparkFilter.java:174)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:668)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:791)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1417)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

The error occurs at Collection<Part> parts = req.raw().getParts();

I looked everywhere, changed some tomcat configs like allowCasualMultipartParsing, but nothing works. This issue has stagnated my project, I have a deadline!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

airinfection picture airinfection  路  3Comments

skyghis picture skyghis  路  3Comments

N0odlez picture N0odlez  路  4Comments

sagesolar picture sagesolar  路  4Comments

dmacvicar picture dmacvicar  路  5Comments