Czachodym
Czachodym

Reputation: 245

Pass an image through JSP and store it in an application

I'm creating a simple website in Spring Boot where i can create an object User that has id and an image and later I can list all the users in a table. The problem is that i don't know how to pass an image from a website to a server or from the server to the website. Setting attribute in HttpServletRequest passes only the id. How to send the image?
Additional question: where should I store all the images? Should I create a folder in resources and save them there or is there a possibility to store images as BLOBs in db? Which option is better and why?

JSP:

<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <th>Id</th>
            <th>Image</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="user" items="${users}">
            <tr>
                <td>${user.id}</td>
                <td>${user.image}</td>
            </tr>
        </c:forEach>
    </tbody>
</table>

Java:

@Data
@Entity(name = "User")
public class User{
    @Id
    @GeneratedValue(starategy = GenerationType.IDENTITY)
    private long id;
    private Image image;
}
@GetMapping("/all-users")
public String allUsers(HttpServletRequest request){
    request.setAttribute("users", usersService.findAll());
    return "index";
}

Upvotes: 0

Views: 68

Answers (3)

horstr
horstr

Reputation: 2837

Let's go through the whole roundtrip from uploading the image to serving the image.

Uploading an image involves reading a multipart MIME POST request from the browser that contains the image amongst other parameters, if any. The request contains the original file name but don't be tempted to use it for storing the image - it's a common anti-pattern leading to security issues. It might even allow an uploader to read and/or overwrite parts of your application or system configuration.

The user has a user ID, so one approach is to store all user images in a dedicated directory in the filesystem based on the filename pattern <userid>.<image-extension>. Then, you could serve the images statically without a servlet (/images/<userid>.<image-extension>) or dynamically with a servlet (which reads the file based on the user ID and writes the data to the HTTP response body) to be able to add access control, for example. If you have other files you need to serve, you could also create a more generic mechanism that serves files based on their hashes (e.g. SHA-1) with URL including the hash (/files/<hash>). For those URLs, you could let the browser cache the contents indefinitely (Cache-Control: immutable) because the response will never change (if the file changes, the hash changes).

Now, as for filesystem vs. database: it all depends on your scenario. If your database is considerably larger than the files you store as BLOBS, it's mostly a matter of preference. By storing all resources in the database, a database backup contains all application data. If you intend to store a large number of BLOBS that represent a considerable percentage of your database, this might change things. A larger database requires more resources and is more complex to deploy. If your database contains BLOBS that almost never change, the value of a database is minimal. BLOB accesses take away from the database page cache, its I/O capacity and increase the size of database backups. In terms of trying to minimize the database size for the sake of performance and manageability, that is a disadvantage. It also means an image has to be transferred from the database to the webserver and, finally, to the client. If you're going the BLOB route, try to use dedicated tables so that the implementation details do not impact inserts/updates of unrelated data. If your application evolves and needs to store and serve large files, BLOBs might become a problem as not all databases provide an API for random access to them, meaning that you'd be forced to read the whole block into one chunk of memory.

Storing files in a simpler storage facility, a filesystem, allows you to take advantage of what a filesystem is good at doing - serving files. If you have to serve a large number of files, you have to think about the directory structure so that you do not create a directory with millions of files. Serving a file in the simplest case means letting the webserver handle a static file request. If the file is in the OS page cache of the webserver, no I/O is involved. Depending on the size of the data set, backups might become more of an issue. A database can be backed up incrementally using a full backup and transaction logs. A filesystem does not provide that kind of facility. That is rarely needed, though. To minimize the window of data loss in case of a corrupted filesystem, you could write to / read from multiple filesystems.

If the expected data set size is small, all those nuances generally do not matter much expect for the security implications I mentioned. As you need to scale, all of those considerations become more and more of an issue and the answers highly depend on how exactly your use cases look like. A general answer to these kind of questions is rarely useful, it can only serve as a guide for further research and planning.

For small-scale deployments, the database route has the advantage that a database backup covers all application data and it might help with avoiding common security pitfalls involved with writing to and reading from the web server filesystem in application use cases.

Upvotes: 1

Alex Chernyshev
Alex Chernyshev

Reputation: 1745

You can inline image's content directly into page:

@Data
@Entity(name = "User")
public class User{
    @Id
    @GeneratedValue(starategy = GenerationType.IDENTITY)
    private long id;
    private Image image;

    public String getIconDataBase64() {
        return image.iconData != null ? java.util.Base64.getEncoder().encodeToString(image.iconData) : null;
    }
}

Then on jsp page:

<table class="table table-striped table-bordered">
    <thead>
        <tr>
            <th>Id</th>
            <th>Image</th>
        </tr>
    </thead>
    <tbody>
        <c:forEach var="user" items="${users}">
            <tr>
                <td>${user.id}</td>
                <td>${user.image}</td>
                <td><img src="data:image/png;base64,${file.iconDataBase64}"/></td>
      </tr>
        </c:forEach>
    </tbody>
</table>

This will work for small images,icons. Please don't try to use it for hi-res images with huge size - browser will crash.

Upvotes: 0

Scary Wombat
Scary Wombat

Reputation: 44854

Usually I would change the code to actually call another servlet which streams the image back to the calling jsp

<td><img src="myServlet?id=${user.id}"/></td>

Upvotes: 0

Related Questions