Upload an image to database

Hi Everyone,

is there a way to upload an image to the database. So when I back up the database the image is backed up as well for example. Or is it only possible to store the string path of the image and to show it everyone time and if so, does this mean I have to create an attribute for the path in the entity. For example I want to upload a profile picture for each employee in a company as well as his birth certificate. So should I store them in a directory and read from that directory or can I upload the image itself to the database. Also, in such example, would it be better to use filemultiupload or fileupload?

Thank you in advance

1 Like

I have similar requirements which led me to this sample … this sample is a very elegant solution IMO. The only difference is I would like to keep the images in an image table related to the User table with a one-to-one relationship and lazy loaded.

It would be nice if there was a simple way of binding an Embedded field to a byte[] attribute and being able to Upload a file into a byte[] attribute. Using files does complicate scaling and backup/restore as you have a file management exercise that needs to be considered. There is also a transaction integrity issue that needs to be considered when using external files.

Given that we have a recipe and if it becomes burdensome, we can always extend the existing Upload and Embedded components.

Thank you for showing me this sample.

Can someone help me with this error:


IllegalArgumentException: Can't find component '[usersTable]'


java.lang.IllegalArgumentException: Can't find component '[usersTable]'
	at com.haulmont.cuba.gui.ComponentsHelper.findAction(ComponentsHelper.java:381)
	at com.haulmont.cuba.gui.ControllerDependencyInjector.getInjectedInstance(ControllerDependencyInjector.java:190)
	at com.haulmont.cuba.gui.ControllerDependencyInjector.doInjection(ControllerDependencyInjector.java:140)
	at com.haulmont.cuba.gui.ControllerDependencyInjector.inject(ControllerDependencyInjector.java:79)
	at com.haulmont.cuba.gui.WindowManager.initWrapperFrame(WindowManager.java:1025)
	at com.haulmont.cuba.gui.WindowManager.createWindow(WindowManager.java:423)
	at com.haulmont.cuba.gui.WindowManager.openWindow(WindowManager.java:591)
	at com.haulmont.cuba.web.WebWindowManager.openWindow(WebWindowManager.java:137)
	at com.haulmont.cuba.gui.config.MenuCommand.execute(MenuCommand.java:100)
	at com.haulmont.cuba.web.sys.MenuBuilder$1.menuSelected(MenuBuilder.java:173)
	at com.vaadin.ui.MenuBar.changeVariables(MenuBar.java:207)
	at com.vaadin.server.communication.ServerRpcHandler.changeVariables(ServerRpcHandler.java:609)
	at com.vaadin.server.communication.ServerRpcHandler.handleInvocations(ServerRpcHandler.java:428)
	at com.vaadin.server.communication.ServerRpcHandler.handleRpc(ServerRpcHandler.java:274)
	at com.vaadin.server.communication.UidlRequestHandler.synchronizedHandleRequest(UidlRequestHandler.java:79)
	at com.haulmont.cuba.web.sys.CubaVaadinServletService$CubaUidlRequestHandler.lambda$synchronizedHandleRequest$113(CubaVaadinServletService.java:314)
	at com.haulmont.cuba.web.sys.CubaVaadinServletService.withUserSession(CubaVaadinServletService.java:196)
	at com.haulmont.cuba.web.sys.CubaVaadinServletService$CubaUidlRequestHandler.synchronizedHandleRequest(CubaVaadinServletService.java:314)
	at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:41)
	at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1424)
	at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:369)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.serviceAppRequest(CubaApplicationServlet.java:278)
	at com.haulmont.cuba.web.sys.CubaApplicationServlet.service(CubaApplicationServlet.java:187)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:292)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.handleNotFiltered(CubaHttpFilter.java:108)
	at com.haulmont.cuba.web.sys.CubaHttpFilter.doFilter(CubaHttpFilter.java:95)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:240)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:207)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:212)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:141)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:616)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:528)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1099)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:672)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1520)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1476)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:745)

I have copied the same exact code found in the sample and I got the above error when I tried to access the employee browse page.

My Code:


package com.company.employees.web.employee;

import com.company.employees.entity.Employee;
import com.haulmont.cuba.gui.app.security.user.browse.UserBrowser;
import com.haulmont.cuba.gui.components.AbstractLookup;
import com.haulmont.cuba.gui.components.Embedded;
import com.haulmont.cuba.gui.components.Table;
import com.haulmont.cuba.gui.components.Embedded.Type;
import com.haulmont.cuba.gui.export.FileDataProvider;
import com.haulmont.cuba.gui.export.ResourceDataProvider;
import com.haulmont.cuba.core.entity.FileDescriptor;


import javax.inject.Inject;
import java.util.Map;

public class EmployeeBrowse extends UserBrowser {

    public static final String DEFAULT_USER_IMAGE_PATH = "/com/company/employees/web/employee/default-avatar.jpg";
    public static final String DEFAULT_USER_IMAGE_NAME = "default-avatar.jpg";


    @Inject
    protected Table<Employee> usersTable;

    @Override
    public void init(Map<String, Object> params) {
        super.init(params);

        usersTable.addGeneratedColumn("image", user-> {
            Embedded embedded = componentsFactory.createComponent(Embedded.class);
            embedded.setType(Type.IMAGE);
            embedded.setWidth("25px");
            embedded.setHeight("25px");

            FileDescriptor userImageFile = user.getImage();
            if (userImageFile == null){
                ResourceDataProvider dataProvider = new ResourceDataProvider(DEFAULT_USER_IMAGE_PATH);
                embedded.setSource(DEFAULT_USER_IMAGE_NAME, dataProvider);
            }
            else {
                FileDataProvider dataProvider = new FileDataProvider(userImageFile);
                embedded.setSource(userImageFile.getId() + "." + userImageFile.getExtension(), dataProvider);
            }

            return embedded;
        });
    }
}

Did you get the sample working before you copied the code? You did not show the screens xml … it needs to look something like:


<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://browseCaption"
        class="com.company.employees.web.employee.EmployeeBrowser"
        extends="/com/haulmont/cuba/gui/app/security/user/browse/user-browse.xml"
        messagesPack="com.company.sample.gui.user"
        xmlns:ext="http://schemas.haulmont.com/cuba/window-ext.xsd">
    <layout spacing="false">
        <groupTable id="usersTable">
            <columns>
                <column id="image"
                        caption="msg://userImage"
                        ext:index="0"
                        width="130px"/>
            </columns>
        </groupTable>
    </layout>
</window>

I would get the sample working first, then extract the code to fit your needs … once you fully understand how it works.

The sample extends the Administration -> Users screen and adds a Tasks screen that uses the avatar you specified for a given User. There is a bit more to this and its not as simple as copying code for the screen as you also need to extend the User entity to add the relationship to the FileDescriptor.

Hi guys,

You can find such kind of behavior in our cuba-vision-clinic project on GitHub. You can find the details of how to store an image into a database using the file upload field component here. A method, that shows image in the embedded component is illustrated here.

Regards,

Aleksey

Here is how my xml file looks like. My table id was employeeTable so I changed it to usersTable, but now I am getting a null pointer exception.


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<window xmlns="http://schemas.haulmont.com/cuba/window.xsd"
        caption="msg://browseCaption"
        class="com.company.employees.web.employee.EmployeeBrowse"
        focusComponent="usersTable"
        messagesPack="com.company.employees.web.employee">
    <dsContext>
        <collectionDatasource id="employeesDs"
                              class="com.company.employees.entity.Employee"
                              view="_local">
            <query>
                <![CDATA[select e from employees$Employee e]]>
            </query>
        </collectionDatasource>
    </dsContext>
    <dialogMode height="600"
                width="800"/>
    <layout expand="usersTable"
            spacing="true">
        <filter id="filter"
                applyTo="usersTable"
                datasource="employeesDs">
            <properties include=".*"/>
        </filter>
        <table id="usersTable"
               multiselect="true"
               width="100%">
            <actions>
                <action id="create"/>
                <action id="edit"/>
                <action id="remove"/>
                <action id="excel"/>
            </actions>
            <columns>
                <column id="nationalId"/>
                <column id="firstName"/>
                <column id="middleName"/>
                <column id="lastName"/>
                <column id="title"/>
                <column id="dateOfBirth"/>
                <column id="gender"/>
                <column id="marital"/>
                <column id="homePhone"/>
                <column id="mobilePhone"/>
                <column id="email"/>
                <column id="organization"/>
                <column id="employeeId"/>
                <column id="employementDate"/>
                <column id="insuranceNumber"/>
                <column id="insuranceStartDate"/>
            </columns>
            <rows datasource="employeesDs"/>
            <rowsCount/>
            <buttonsPanel id="buttonsPanel"
                          alwaysVisible="true">
                <button id="createBtn"
                        action="usersTable.create"/>
                <button id="editBtn"
                        action="usersTable.edit"/>
                <button id="removeBtn"
                        action="usersTable.remove"/>
                <button id="excelBtn"
                        action="usersTable.excel"/>
            </buttonsPanel>
        </table>
    </layout>
</window>

The null pointer exceptions were produced because I changed the extend class from AbstractLookup to UserBrowser as was found in the sample. Same thing for the edit screen. So how can I apply the same thing without extending UserBrowser?

Thanks Aleksey!

Coming from LightSwitch there is a lot of boilerplate code to make a β€˜Toy’. It would be nice if the handling of image in database blob was a little more seamless. If you want convert LS users, this would be a simple thing that makes conversions easier.

Just my 2c.