ACV
ACV

Reputation: 10562

Spring MVC upload a file and then provide a link for downloading

I have Spring MVC web app running on Tomcat.

I upload a file and save it in the /tmp folder on the file system.

Then I need to show a link to that file in the view (Thymeleaf), so that the user can download the file by clicking on the link. How to do that?

I've heard about configuring Tomcat to allow a specific context to link to a folder on the FS, but not sure how to do that or if that is the only solution. Please help.

Upvotes: 0

Views: 2654

Answers (2)

ACV
ACV

Reputation: 10562

This is the precisely setup that worked for me (Tomcat 8, SpringMVC, boot):

  1. server.xml: <Context docBase="C:\tmp\" path="/images" />

  2. In the controller:

    public String createNewsSource(@ModelAttribute("newsSource") NewsSource source, BindingResult result, Model model,
            @RequestParam("attachment") final MultipartFile attachment) {
        new NewsSourceValidator().validate(source, result);
        if (result.hasErrors()) {
            return "source/addNewSource";
        }
        if (!attachment.isEmpty()) {
            try {
                byte[] bytes = attachment.getBytes();
                BufferedOutputStream stream = new BufferedOutputStream(
                        new FileOutputStream(new File("/tmp/" + attachment.getOriginalFilename())));
                stream.write(bytes);
                stream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        source.setLogo("images/" + attachment.getOriginalFilename());
        newsSourceService.createNewsSourceIfNotExist(source);
        return "redirect:/sources/list";
    }

As you can see I am saving the file to /tmp, but in the DB (source.setLogo()), I am pointing to images as mapped in server.xml

Here's where I found about Tomcat config:

If the images are all located outside the webapp and you want to have Tomcat's DefaultServlet to handle them, then all you basically need to do in Tomcat is to add the following Context element to /conf/server.xml inside tag:

This way they'll be accessible through http://example.com/images/....

SO answer to a similar question

Upvotes: 0

Kejml
Kejml

Reputation: 2204

The way I approach this is slightly different. Basically I use two controller actions for handling file uploads, one for uploading, and for downloading (viewing) files.

So upload action would save files to some preconfigured directory on the file system, I assume you already have that part working.

Then declare download action similar to this

@Controller
public class FileController {
     @RequestMapping("/get-file/{filename}")
     public void getFileAction(@RequestParam filename, HttpServletResponse response) {
         // Here check if file with given name exists in preconfigured upload folder
         // If it does, write it to response's output stream and set correct response headers
         // If it doesn't return 404 status code
     }
 }

If you want to make impossible to download file just by knowing the name, after uploading file, save some meta info to the database (or any other storage) and assign some hash (random id) to it. Then, in getFileAction, use this hash to look for file, not the original filename.

Finally, I would discourage using /tmp for file uploads. It depends on the system/application used, but generally temp directory are meant, as name suggest, for temporary data. Usually it is guaranteed data in the temp directory will stay for "reasonable time", but applications must take into account that content of temp directory can be deleted anytime.

Upvotes: 1

Related Questions