Downloading csv file using Play Framework? - scala

I want to add to my app a simple button that on click will call an Action that will create a csv file from two lists I have and download it to the user computer.
This is my Action:
def createAndDownloadFile = Action {
val file = new File("newFile.csv")
val writer = CSVWriter.open(file)
writer.writeAll(List(listOfHeaders, listOfValues))
writer.close()
Ok.sendFile(file, inline = false, _ => file.getName)
}
but this is now working for me, the file is not getting downloaded from the browser...
im expecting to see the file get downloaded by the browser, i thought Ok.sendFile should do the trick..
thanks!

You can use Enumerators and streams for that. It should work like this:
val enum = Enumerator.fromFile(...)
val source = akka.stream.scaladsl.Source.fromPublisher(play.api.libs.streams.Streams.enumeratorToPublisher(enum))
Result(
header = ResponseHeader(OK, Map(CONTENT_DISPOSITION → "attachment; filename=whatever.csv.gz")),
body = HttpEntity.Streamed(source.via(Compression.gzip), None, None)
)
This will actually pipe the download through gzip. Just remove the .via(Compression.gzip) part if that is not needed.

Related

Scala changing parquet path in config (typesafe)

Currently I have a configuration file like this:
project {
inputs {
baseFile {
paths = ["project/src/test/resources/inputs/parquet1/date=2020-11-01/"]
type = parquet
applyConversions = false
}
}
}
And I want to change the date "2020-11-01" to another one during run time. I read I need a new config object since it's immutable, I'm trying this but I'm not quite sure how to edit paths since it's a list and not a String and it definitely needs to be a list or else it's going to say I haven't configured a path for the parquet.
val newConfig = config.withValue("project.inputs.baseFile.paths"(0),
ConfigValueFactory.fromAnyRef("project/src/test/resources/inputs/parquet1/date=2020-10-01/"))
But I'm getting a:
Error com.typesafe.config.ConfigException$BadPath: path parameter: Invalid path 'project.inputs.baseFile.': path has a leading, trailing, or two adjacent period '.' (use quoted "" empty string if you want an empty element)
What's the correct way to set the new path?
One option you have, is to override the entire array:
import scala.collection.JavaConverters._
val mergedConfig = config.withValue("project.inputs.baseFile.paths",
ConfigValueFactory.fromAnyRef(Seq("project/src/test/resources/inputs/parquet1/date=2020-10-01/").asJava))
But a more elegant way to do this (IMHO), is to create a new config, and to use the existing as a fallback.
For example, we can create a new config:
val newJsonString = """project {
|inputs {
|baseFile {
| paths = ["project/src/test/resources/inputs/parquet1/date=2020-10-01/"]
|}}}""".stripMargin
val newConfig = ConfigFactory.parseString(newJsonString)
And now to merge them:
val mergedConfig = newConfig.withFallback(config)
The output of:
println(mergedConfig.getList("project.inputs.baseFile.paths"))
println(mergedConfig.getString("project.inputs.baseFile.type"))
is:
SimpleConfigList(["project/src/test/resources/inputs/parquet1/date=2020-10-01/"])
parquet
As expected.
You can read more about Merging config trees. Code run at Scastie.
I didn't find any way to replace one element of the array with withValue.

File upload, declaration via apiOperation (Swagger and Scalatra 2.6)

There is an existing project that uses Scalatra (2.6) and Swagger:
scalaMajorVersion = '2.12'
scalaVersion = "${scalaMajorVersion}.8"
scalatraVersion = "${scalaMajorVersion}:2.6.4"
compile "org.scalatra:scalatra-swagger_${scalatraVersion}"
I easily could add a new end point like:
get ("/upload", op[String]( // op finally invokes apiOperation
name = "Test method",
params = List(
query[Long]("id" -> "ID"),
query[String]("loginName" -> "login name")
),
authorizations = List(Permission.xxxxx.name)
)) {
...
}
but I cannot upload a file.
I expect to see a file selector button, but instead I see a single-line edit field.
(There are numerous things I'm uncertain about: form or file, [String] or [FileItem], which trait(s), what kind of initialization, etc.)
In the existing code I found a comment that someone could not get swagger to handle file upload. At the same time, I read that Scalatra and Swagger can do that, not all versions of them, but it looks like the version used in the project should be able to do that.
I could find code examples with yml/json interface definitions, but in the project there is no yml, only the apiOperation-based stuff.
Is there a working example using Scalatra 2.6, Swagger, and apiOperation?
I managed to get the file chooser (file selector, "Browse") button; there was no predefined constant (like DataType.String) for that. After I used DataType("File"), everything else just worked.
https://docs.swagger.io/spec.html says:
4.3.5 File
The File (case sensitive) is a special type used to denote file
upload. Note that declaring a model with the name File may lead to
various conflicts with third party tools and SHOULD be avoided.
When using File, the consumes field MUST be "multipart/form-data", and
the paramType MUST be "form".
post ("/uploadfile", op[String]( // op finally invokes apiOperation
name = "Upload File",
params = List(
new Parameter(
`name` = "kindaName",
`description` = Some("kindaDescription2"),
`type` = DataType("File"), // <===== !!!!!!
`notes` = Some("kindaNotes"),
`paramType` = ParamType.Form, // <===== !!
`defaultValue` = None,
`allowableValues` = AllowableValues.AnyValue,
`required` = true
)
),
consumes = List("multipart/form-data"), // <===== !!
...
)) {
val file: FileItem = fileParams("kindaName") // exception if missing
println("===========")
println("file: " + file)
println("name: " + file.getName + " size:"+file.getSize+" fieldName:"+file.getFieldName+ " ContentType:"+file.getContentType+" Charset:"+file.getCharset)
println("====vvv====")
io.copy(file.getInputStream, System.out)
println("====^^^====")
val file2: Option[FileItem] = fileParams.get("file123") // None if missing, and it is missing
println("file2: " + file2)
PS the apiOperation stuff is called "annotations".

Is there a way to set a destination to unzip for gzip files when a user double clicks on the archive. Looking for a Scala/java solution

Is there a way to get a gzip archive file to unzip to a different destination when a user double clicks on the archive? Currently, my compression code looks something like this in Scala:
val filename = SetFilename.getOrElse {
val path = files.head.getAbsolutePath
val baseUrl = FilenameUtils.getFullPathNoEndSeparator(path)
...
}
val output = new File(filename)
val fos = new FileOutputStream(output)
val gzos = new GZIPOutputStream(new BufferedOutputStream(fos))
try {
files.foreach { input =>
val fis = new FileInputStream(input)
try {
ioStream(fis, gzos)
gzos.flush()
}
finally {
fis.close()
}
}
}
finally {
gzos.close()
fos.close()
}
IS there any way to tell the compressed files to decompress in a different destination when a user double clicks on the archive?
It is not the gzip archive that decides it will be unzipped in the same location, it's something the operating system you're unzipping it on decides.
If you need to unzip into a specific place, you should look for a packaging solution like deb for Ubuntu or Debian systems; or dmg for OSX.

Parse a XES file in Scala

I am trying since few hours to parse an XES file using the Deckfour XES Open library, I want to have the logs of my file however I can't find any parser which parse my .xes
//Getting Xes File
val logXesFile = request.body.file("file").get
val filename = logXesFile.filename
logger.debug(s"filename: $filename")
//Try all available parsers
val otherParsers = XParserRegistry.instance().getAvailable
for(p <- otherParsers){
try{
logger.debug(p.name())
val logs = p.parse(logXesFile.ref.file)
if(logs.size() > 0){
logger.debug(s"Parser founded ! $p")
}
}
catch{
case e : Exception => {logger.debug("Exception !")}
}
}
Here is a screenshot of my debugger :
Also I tried plenty of xes files the problem are definitely not from those, I also tried with the GZIP parser
Ok I finally found !
The "myfile".ref.file allow indeed to get the file however it change the name of the file to something like "multipartBody26...TemporaryFile" and the parser function from the deckfour library do not recognize it as a ".xes" anymore while the content is still an xes file.
So the solution looks to be to rename the file

How do I save the file name in the database

When I add a new record in a database using a form, I also can upload an image. These two are not linked together; record goes in database and the image goes in an folder on my desktop, so to know which image belongs to which record, I want to put the filename in a column. How do i approach this?
Im using PlayFramework 2.4, Scala, H2 Database and Anorm for my Project
In your html form you need to have an input tag of file type, something like:
<input type="file" name="picture">
And in your scala method Controller, where you are getting the form submit, something like:
def save = Action(parse.multipartFormData) { request =>
request.body.file("picture").map { picture =>
import java.io.File
val filename = picture.filename
println(filename)
Ok("saved")
}
You could retrieve the absolute path of the file like so:
scala> val filePath = getClass.getResource("myImage.png")
filePath: java.net.URI = file:/home/robert/myImage.png