How to overwrite fileStorageDir? - Inaccessible primary storage

Our app-portal client needs to access images uploaded from web client app. The default fileStorageDir in core module is configured to save the uploads to (on Windows):


whereas the app-portal accesses the image in ftl like this:

<img src="../../resources/img/sc/t_0730852951112.jpg" />

Here the image path “…/…/resources/img/sc/t_0730852951112.jpg” is hardcoded to test in app-portal.

Now we can upload jpg files (I can see files at: \build\tomcat\work\app-core\filestorage). We want to use the actual uploaded image instead of hardcoded one.

The highest level of folder, this “…/…/resources…” mechanism, can reach is app-portal folder in tomcat\webapps. It can NOT access \build\tomcat\work\app-core\filestorage where images are uploaded to. So what I thought to do is change the fileStorageDir to \build\tomcat\webapps\app-portal\resources\img.

What I have tried is to add a line in <…>\modules\core\src\


Then I got error:

ERROR - Inaccessible primary storage at \webapps\app-portal\resources\img

How can I overwrite fileStorageDir to access \build\tomcat\webapps…? Is there any difference to specify fileStorageDir value on Windows and Linux?

Thanks a lot,

1 Like


First of all, you have to create file storage directory manually. Then you can specify file storage location using absolute or relative path:

cuba.fileStorageDir = ${catalina.base}/demo-file-storage # relative to tomcat root folder
# or
cuba.fileStorageDir = /home/user/work/files # absolute storage location


By default, portal does not have a Spring MVC Controller that can expose files or images to web browser. You have to create it manually in your project.

This controller doesn’t have to read files from a disk manually since your application can be deployed in a distributed configuration.

There is a special infrastructure interface FileLoader that can load binary data of files to client. Let’s use it to show images in a portal application. See also:

First of all we need to create a controller that will load binary data from a middleware when a web browser requests image content:

public class PngFilesController {

    private final Logger log = LoggerFactory.getLogger(PngFilesController.class);

    protected UserSessionService userSessionService;

    protected DataService dataService;

    protected FileLoader fileLoader; // file loader is used to load file data from middleware to clients

    public void downloadImage(@PathVariable String fileId,
                              @RequestParam(required = false) Boolean attachment,
                              HttpServletResponse response) throws IOException {
        UUID uuid;
        try {
            uuid = UUID.fromString(fileId);
        } catch (IllegalArgumentException e) {
            response.sendError(HttpStatus.BAD_REQUEST.value(), "Invalid entity ID");
        LoadContext<FileDescriptor> ctx = LoadContext.create(FileDescriptor.class).setId(uuid);
        FileDescriptor fd = dataService.load(ctx);

        // We will send only PNG files here
        if (fd == null || !"png".equals(fd.getExtension())) {


        try {
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            response.setDateHeader("Expires", 0);
            response.setHeader("Content-Type", getContentType(fd));
            response.setHeader("Content-Disposition", (BooleanUtils.isTrue(attachment) ? "attachment" : "inline")
                    + "; filename=\"" + fd.getName() + "\"");

            ServletOutputStream os = response.getOutputStream();
            try (InputStream is = fileLoader.openStream(fd)) {
                IOUtils.copy(is, os);
        } catch (Exception e) {
            log.error("Error on downloading the file {}", fileId, e);


    protected String getContentType(FileDescriptor fd) {
        if (StringUtils.isEmpty(fd.getExtension())) {
            return FileTypesHelper.DEFAULT_MIME_TYPE;

        return FileTypesHelper.getMIMEType("." + fd.getExtension().toLowerCase());

Here we simply load binary data and write it to HTTP response. Please note that you have to setup Access groups (including anonymous user)
with proper security settings or check security manually in the controller.

After that, let’s load all the PNG images from a database and show them on index page.

Portal controller:

public class PortalController {
    protected DataService dataService;

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String index(Model model) {
        // load all .png files from db
        List<FileDescriptor> files = dataService.loadList(LoadContext.create(FileDescriptor.class)
                .setQuery(new LoadContext.Query("select fd from sys$FileDescriptor fd")));
        List<UUID> fileIds =
                .filter(fd -> "png".equals(fd.getExtension()))

        model.addAttribute("fileIds", fileIds);

        return "index";

Add the following code to index.html thymeleaf template:

<h1>All .png images from FileStorage</h1>
<ul th:each="fileId: ${fileIds}">
        <img th:src="@{${'images/'+ fileId}}"/>

The last option we have to set is to setup Spring Security options in portal-security-spring.xml:

    <!-- We will simply enable anonymous access to images,
         but you can decide if want to do it or not -->
    <http pattern="/images/**" security="none"/>

Now we can restart the application and upload a couple of PNG images using web interface.
Finally, open index page of portal - and we will see our images.

You can find complere sample project on GitHub:

This is a smart way. I will follow and try it out.

Thank you Yuriy.


Your solution works like a charm. I am able to access uploaded images from portal module.

As related, how to specify the default file storage path in core module’s modules\core\src\ According to:

The default root path can be specified in like:

then I got:
ERROR - Inaccessible primary storage at \uploadedFiles

If i do NOT define “cuba.fileStorageDir=” line, I got the uploaded file at:

What’s the correct way to specify cuba.fileStorageDir value?


Thank you very much.