Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to upload a huge file in play framework?

In my application i am trying to upload a file to the destination folder, the browser doesn't allow me to upload the file of size greater than 4 GB and my file size is 15 GB.I am struck here and have no idea how to upload it.Any help is highly appreciable.

like image 918
b singh Avatar asked Dec 01 '22 18:12

b singh


1 Answers

You can solve this problem in two stage:

  1. You need to split huge file into smaller chunks and then send them to the server. To achieve this you may use some javascript libraries, for example Resumable.js
  2. In your play2 controller you need to assemble these chunks using Iteratee API and then your can do what ever you want with your file.

EDIT:

Let's use resumable.js as example for the client side, I will not dive into the details, you can find a documentation and the examples here

Our view will be minimalist(only link to select a file or files) :

@()
@main("File upload"){
    <a href="#" id="browseButton">Select files</a>
}

Our javasctipt :

$(function(){
  var r = new Resumable({
    target:'/test/upload'
  });

  r.assignBrowse(document.getElementById('browseButton'));

  r.on('fileSuccess', function(file){
    console.debug(file);
  });
  r.on('fileProgress', function(file){
    console.debug(file);
  });
  // more events, look API docs
});

Our main.scala.html :

@(title: String)(content: Html)

<!DOCTYPE html>

<html>
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
        <link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/style.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.at("images/favicon.png")">
        <script src="@routes.Assets.at("javascripts/jquery-1.9.0.min.js")" type="text/javascript"></script>
        @*We include resumable.js library*@
        <script src="@routes.Assets.at("javascripts/resumable.js")" type="text/javascript"></script>
        @*Our javascript for file upload*@
        <script src="@routes.Assets.at("javascripts/upload.js")" type="text/javascript"></script>
    </head>
    <body>
        @content
    </body>
</html>

And our server side, in play controller :

First we need to create a function that will treat our file part by consuming our chanks and producing the result - Array[Byte] in our example.

 // hadle file part as Array[Byte]
  def handleFilePartAsByteArray: PartHandler[FilePart[Array[Byte]]] =
    handleFilePart {
      case FileInfo(partName, filename, contentType) =>
        // simply write the data to the a ByteArrayOutputStream
        Iteratee.fold[Array[Byte], ByteArrayOutputStream](
          new ByteArrayOutputStream()) { (os, data) =>
          os.write(data)
          os
        }.mapDone { os =>
          os.close()
          os.toByteArray
        }

then we can define a custom body parser:

// custom body parser to handle file part as Array[Byte]
  def multipartFormDataAsBytes:BodyParser[MultipartFormData[Array[Byte]]] =
    multipartFormData(handleFilePartAsByteArray)

and in the end our controller may looks like:

def handleFileUpload = Action(multipartFormDataAsBytes){ request =>
 // retrieve file name from data part   
 val fileName  = request.body.asFormUrlEncoded.get("resumableFilename").get.headOption
 // retrieve arrays of byte from file part and write them to file
    request.body.files foreach{
      case FilePart(key,filename,content,bytes)=>
        import scalax.io._
        val output:Output = Resource.fromFile(fileName.getOrElse("default"))
        output.write(bytes)
    }
    Ok("")
  }

List of imports for the controller :

import play.api.mvc._
import play.api.mvc.BodyParsers.parse.Multipart._
import play.api.libs.iteratee.Iteratee
import java.io.ByteArrayOutputStream
import play.api.mvc.BodyParsers.parse._
import play.api.mvc.BodyParsers.parse.Multipart.FileInfo
import play.api.mvc.MultipartFormData.FilePart
like image 172
arussinov Avatar answered Mar 12 '23 11:03

arussinov