javaspring-bootspring-securityfile-uploadthymeleaf

How to load images outside of project in spring boot


I have a spring boot app where users can login and upload images. The upload works fine, but i cant make it work when it comes to show the image from here: /var/webapp/images/, which will be in the root of the disk (outside the project). I tried some related questions, but with no luck or couldnt even understand the answer.

To know:

  1. I use spring security too and this could be usefull:
http.authorizeHttpRequests(auth -> {
     auth.requestMatchers("/login", "/register", "/css/**", "/js/**", "/images/**").permitAll();
})
  1. The images are shown like this with thymeleaf (the user.picture is an url to the picture):
<img th:src="${user.picture}">
  1. My saveImage function:
private String IMAGES_ROOT = "/var/webapp/images/";

public void saveImage(MultipartFile image) throws IOException {
        String currentUsername = SecurityUtil.getSessionUser();
        UserEntity user = userRepository.findByName(currentUsername);
        if (!image.isEmpty()) {
            if (user.getPicture() != null) {
                Files.deleteIfExists(Paths.get(user.getPicture()));
            }
            Path imagePath = Paths.get( IMAGES_ROOT + user.getId().toString());
            if (!Files.exists(imagePath)) {
                Files.createDirectories(imagePath);
            }
            user.setPicture(Paths.get(imagePath + File.separator + image.getOriginalFilename()).toString());
            Files.copy(image.getInputStream(), Paths.get(imagePath + File.separator + image.getOriginalFilename()));
            userRepository.save(user);
        }
    }
  1. On frontend side i just get a 404 not found error
  2. Also, i want to use the external folder (/var/webapp/images/) and the resources/static/images folder too.
  3. Tried adding addResourceHandlers, but couldnt find answer where i have spring security and also 2 resource and how to distinguish them

So basically i want to access a folder, for example: i have my app in here: D:/files, then i want to access my folder which should be here D:/var/webapp/images/ and this should work if the disk is C or E or whatever.

Most of the questions related to this are a couple years old or tried and didnt work, for example this: spring.resources.static-locations = classpath:/static/,file:/var/webapp/images/ in the properties file


Solution

  • There are two things to say here

    Understanding file systems

    Because you talk about "D:.. C or E" this sounds like you are referring to Windows Drives.

    Yet your code refers to /var/webapp/images/

    If you had a Unix-based Operating system (including Mac) then there are no Drive letters. The location of all files accessible by the machine start with a /. If you have a Windows OS, then its a path start with / is ambiguous. It will likely treat that as root of the Drive which the application was started on. Ideally you should quality the path with the drive letter, i.e.

    private String IMAGES_ROOT = "D:/var/webapp/images/";
    

    (or inject this as a property to your application)

    Understanding the class path

    The classpath is a location of where the "top" of a hierarchy of classes can be found (so a java class com.acme.MyController will be found in a folder com/acme one of the classpath locations). Besides classes, the classpath resource loader can also provide other non-class assets like text files or images.

    However, if you are building you application into a Web archive files (.war) or Java archive file (.jar) then that structured set of files is inside a that container. In these cases you cannot write into that container file. I am not wholly sure about loading from the classpath, but dynamically loading from the classpath may not work if that the contents are changing - and is often considered bad since you should keep your application data away from the code.

    I recommend you read up about non-claspath Resource Loaders, which Spring does support. Note though that you may have to specify the Windows Drive letter like this: "file:///D:/var/webapp/images/". Here is an article: https://www.baeldung.com/spring-mvc-static-resources#2-serving-a-resource-stored-in-the-file-system

    You can certainly write yourself a Spring endpoint that takes a path and reads some arbitrary location in the file system (e.g. IMAGES_ROOT), opens a file and streams it. This is a bit involved as you will need to consider what MimeType to offer the stream, otherwise a web browser may not wish to render the image.