Rotzlucky Rotzlucky - 1 year ago 45
Java Question

Play: How to prevent 404 on image src on first request after upload

I've got a simple form with 2 text and 1 file input

@helper.form(action = routes.CharactersController.newCharacter(), 'enctype -> "multipart/form-data") {
@helper.inputText(field = characterForm("characterName"))
@helper.inputText(field = characterForm("characterRealName"))
@helper.inputFile(field = characterForm("characterImage"))

<input type="submit">

in the newCharacter action I move the file to the asset folder.

Http.MultipartFormData body = request().body().asMultipartFormData();
Http.MultipartFormData.FilePart<File> picture = body.getFile("characterImage");
File file = picture.getFile();
file.renameTo(new File(LinkUtil.getCharacterUploadPath(), imageName + ".jpg"));

and then make a redirect to the /show page to display the new entered character

return redirect(,;

On the newly loaded page I only get a broken image and see a 404 in the console for the image. When I reload the page the image is rendered as expected.

So which part isn't ready when I redirect? Is it the renameTo or does play need some time to find the new asset?
And how can I ensure everything is ready before I redirect?

To improve my question

I'm using play 2.5

LinkUtil.getCharacterUploadPath() resolves to "public/images/characters/"

I'm embedding the image in my show template like this:

<img src="@character.getImagePath">

which then resolves to "/assets/images/characters/imagename.jpg"

Edit 2: Further investigations:

When I check the filesystem within the newCharacter action with something like file.exists() for the new path, I get true. So the rename/moving part seems finished.
However if I try to get the resource via environment


it's null. Even within a while loop the resource stays null. Which seems to indicate that the file isn't really added to the classpath yet. Unfortunately no matter how long I stay in the action the resource is always null. But when I put the Thread to sleep for 2000ms and then check the resource again in the show action it gets resolved properly. But only if I wait for 2s.

Another test was to utilize a onerror="window.location.reload()" on the image. Which results in about 5-10 reloads before the image is found.

So it's definetely a matter of time, but putting the Thread to sleep for an arbitrary amount feels plain wrong.
There has to be a way to be sure that the file is available on the classpath.

Every hint/idea is appreciated.

Answer Source

I got it to work! First my approach above was flawed.

Taking uploaded images, putting them into the public folder and trying to serve them with the AssetController won't work on a production build. Everything in the public folder is packaged into a .jar file. And some quick test hinted that my renameTo from above won't put the images where I expected them to be.

So here my solution:

Define a new route:

GET     /files/*file    controllers.Images.serve(file)

In the action use an absolute path on the filesystem

public class Images extends Controller {
    public Result serve(String file) {
        return ok(new"/var/www/files/" + file));

Access the file with something like this

<img src="/files/images/characters/imagename.jpg">

The Image is accessible instantly on the redirect request.

My guess about the delay in the Question is, that play tries to compile everything that is added to the classpath before making it accessible