3
votes

I'm trying to allow users to upload photos to the server and then view them (all users can view all photos) in production (NOT development). While in development mode everything is simple - I can upload the files to the public folder and then read then from there, in production mode I don't have access to the public folder anymore (as this approach is for static accesses and not dynamic).

So, I have 2 issues:

  1. Upload: currently I can't understand how to save the uploaded photos to a specific folder, without using an absolute path to the location where I want the photos to be saved.

Here is the code for upload (similarly to this guide - https://www.playframework.com/documentation/2.4.x/ScalaFileUpload):

def uploadPhoto = Action(parse.multipartFormData) { request =>
  import play.api.mvc.MultipartFormData
  import play.api.libs.Files.TemporaryFile
  import java.io.File
  import java.nio.file.Path
  import java.nio.file.Paths

  try {
    val multipartForm: MultipartFormData[TemporaryFile] = request.body

    val pathDev: Path = Paths.get("./public/img");
    val pathProduction: Path = Paths.get("/...<[full path]>.../public/img");
    val imgPath =
      if (Files.exists(pathDev)) { pathDev.toString() } 
      else { pathProduction.toString() }

    val newFile = img.get.ref.moveTo(new File(s"$imgPath/$imgName"))
    // [HERE I save the path to my DB]
    Ok(Json.obj("imgName" -> imgName))

  } catch {
    case e: Exception =>
      BadRequest("unknown error")
  }
}
  1. It is unclear to me how to serve the uploaded images back to users that want to see them. I want to dynamically change the scr in the img html tag to show the relevant image, like so: $("#img").attr("src",'assets/img/1.jpg');

But as the public folder is not available, the images are "not there" (at least until I will "stage" the project and re-run it - https://www.playframework.com/documentation/2.4.x/Assets).

I tried the following approach (https://www.playframework.com/documentation/2.4.x/ScalaStream): I have added the following line to my conf/routes file: GET /img/:filename controllers.MyController.getPhoto(filename)

and defined the following function in the controller: def getPhoto(filename: String) = Action { Ok.sendFile(new java.io.File("./img/" + filename)) }

But the browser is downloading the file instead of showing it...

These are related: Handling dynamic created files in play 2 How to serve uploaded files in Play!2 using Scala?

Any assistance will be very appropriated.

2

2 Answers

2
votes

Here's how I fix this

ISSUE 1

For upload file path, in play you can define configurations in conf/application.conf file, and you can use different file for production mode, using -Dconfig.file=/path/to/the/file.

So I defined an attribuite called myapp.image.base, in debug mode just set it to "", and in production mode (I created a file called conf/application.prod.conf) , I put an absolute path to it. So in my code, I always use the following command for file path (it's in Java, but you should find a similar way in Scala for reading configuration)

Play.application().configuration().getString("myapp.image.base")+"img" 

ISSUE 2

For serving image You need to create a router. First in your routes file, add something like this:

GET /user/images/:name controllers.Application.imageAt(name:String)

And write a simple file reader in action imageAt which return the file in stream. Again my sample is in Java but you should archive the same using Scala

    File imageFile = new File(ReportFileHelper.getImagePath());
    if (imageFile.exists()) {
    //resource type such as image+png, image+jpg
        String resourceType = "image+"+imageName.substring(imageName.length()-3);
        return ok(new FileInputStream(imageFile)).as(resourceType);
    } else {
        return notFound(imageFile.getAbsoluteFile());
    }

After that, the images is reachable from url /user/images/

1
votes

Play reads files off the classpath (which includes the assets directory). On the JVM, the classpath is immutable—once started, files added to folders on the classpath will not actually be added to the classpath.

This works in development mode because Play reloads the classpath whenever it detects a change. That same hot-reloading is not enabled in production (for good reason).