0

I'm creating an image upload API that takes files with POST requests. Here's the code:

def upload = Action(parse.temporaryFile) { request =>
    val file = request.body.file
    Ok(file.getName + " is uploaded!")
  }

The file.getName returns something like: requestBody4386210151720036351asTemporaryFile

The question is how I could get the original filename instead of this temporary name? I checked the headers. There is nothing in it. I guess I could ask the client to pass the filename in the header. But should the original filename be included somewhere in the request?

yang
  • 498
  • 5
  • 22

3 Answers3

3

All the parse.temporaryFile body parser does is store the raw bytes from the body as a local temporary file on the server. This has no semantics in terms of "file upload" as its normally understood. For that, you need to either ensure that all the other info is sent as query params, or (more typically) handle a multipart/form-data request, which is the standard way browsers send files (along with other form data).

For this, you can use the parse.multipartFormData body parser like so, assuming the form was submitted with a file field with name "image":

def upload = Action(parse.multipartFormData) { request =>
  request.body.file("image").map { file =>
    Ok(s"File uploaded: ${file.filename}")
  }.getOrElse {
    BadRequest("File is missing")
  }
}

Relevant documentation.

Mikesname
  • 8,781
  • 2
  • 44
  • 57
1

It is not sent by default. You will need to send it specifically from the browser. For example, for an input tag, the files property will contain an array of the selected files, files[0].name containing the name of the first (or only) file. (I see there are possibly other properties besides name but they may differ per browser and I haven't played with them.) Use a change event to store the filename somewhere so that your controller can retrieve it. For example I have some jquery coffeescript like

    $("#imageFile").change ->
        fileName=$("#imageFile").val()
        $("#imageName").val(fileName)

The value property also contains a version of the file name, but including the path (which is supposed to be something like "C:\fakepath" for security reasons, unless the site is a "trusted" site afaik.)

(More info and examples abound, W3 Schools, SO: Get Filename with JQuery, SO: Resolve path name and SO: Pass filename for example.)

Community
  • 1
  • 1
wwkudu
  • 2,778
  • 3
  • 28
  • 41
1

As an example, this will print the original filename to the console and return it in the view.

    def upload = Action(parse.multipartFormData(handleFilePartAsFile)) { implicit request =>

    val fileOption = request.body.file("filename").map {

      case FilePart(key, filename, contentType, file) =>
        print(filename)
        filename   
    }
    Ok(s"filename = ${fileOption}")
  }

  /** 
   *  Type of multipart file handler to be used by body parser 
   */    
  type FilePartHandler[A] = FileInfo => Accumulator[ByteString, FilePart[A]]

  /**
   * A FilePartHandler which returns a File, rather than Play's TemporaryFile class.
   */
  private def handleFilePartAsFile: FilePartHandler[File] = {

    case FileInfo(partName, filename, contentType) =>
      val attr = PosixFilePermissions.asFileAttribute(util.EnumSet.of(OWNER_READ, OWNER_WRITE))
      val path: Path = Files.createTempFile("multipartBody", "tempFile", attr)
      val file = path.toFile
      val fileSink: Sink[ByteString, Future[IOResult]] = FileIO.toPath(file.toPath())
      val accumulator: Accumulator[ByteString, IOResult] = Accumulator(fileSink)

      accumulator.map {
        case IOResult(count, status) =>
          FilePart(partName, filename, contentType, file)
      } (play.api.libs.concurrent.Execution.defaultContext)
  }
jacks
  • 4,614
  • 24
  • 34