Skip to main content

Upload CSV file using opencsv with Play 2.0.2, Scala and MongoDB

Hi Everyone...

Today I'm going to explain you that how we could upload a CSV file with list of products to the MongoDB table. The way I'm describing here might me inefficient but still it is useful.

We know that Scala is running on JVM and so that we can use any java library with Scala. I will use OpenCSV as a CSV parser for this program. OpenCSV is a CSV parser library for Java. No matter it is for java here we will use it with Scala.

First we should have MongoDB up and running. Include following opencsv dependency in your build file. I'm using SBT as building tool.
"net.sf.opencsv" % "opencsv" % "2.1"
Here next I will create view file. Name of this file will be 'index.scala.html'.
@(message: String)(implicit request: RequestHeader, 
flash:Flash, l: Lang=Lang("en"))

@import play.api.i18n._
@import views.html.helper._

@main(message, null) { 
 //print messages
 @if(flash.data.contains("message")){ 
   <div class="alert alert-success">@flash.get("message")</div>
 } 
 //print error messages 
 @if(flash.data.contains("errormessage")){  
   <div class="alert alert-error">@flash.get("errormessage")</div>
 }
 //create Play form
 @form(action = controllers.routes.ProductUpload.upload, 'enctype -> 
       "multipart/form-data") {
   <input type="file" name="fileupload">
   <p>
     <input type="submit" value="Upload CSV" class="btn btn-primary">
   </p>
 }
}
Next step is to add routes to my routes config file. I ll add two routes, one for load the screen with upload form and other to post CSV file to the back end to process.
//This get request to load upload form
GET  /productupload        controllers.ProductUpload.index
//This post request for send csv file to back end
POST /productupload/upload controllers.ProductUpload.upload
Now Here is the most interesting and important part,the controller. Here I have created a controller to handle the actual process(ProductUpload.scala).
object ProductUpload extends Controller{
  //I have hard code here the file path
  val filePath = "\\conf\\data\\"

  //Handling default requests. to load product form
  def index = Action {  implicit request =>
    Ok(views.html.productupload.index(""))
  }

  //
  def upload = Action(parse.multipartFormData) { request =>
    request.body.file("fileupload").map { file =>
    val filename = file.filename
    val contentType = file.contentType
    
    //moving csv file to application
    file.ref.moveTo(new File(Play.application.path + 
                    filePath + filename), true)

    //uploading products
    uploadProducts(filename)
    
    //send message
    Redirect(controllers.routes.ProductUpload.index).
      flashing("message" -> "Products uploaded successfully !!!")
    }.getOrElse {
      //send error message
      Redirect(controllers.routes.ProductUpload.index).
       flashing("errormessage" -> "File Missing")
    }
  }

  //function to upload products
  def uploadProducts(fileName : String) = {
    //Inserting products to the DB using specified CSV
    val productReader = new CSVReader( new FileReader( Play.application.
        path + filePath + fileName ) )
    var x = 0;
    for ( row <- productReader.readAll ) {
      //if check to skip first row(headings)
      if(row(0) != "" && x != 0){
        
        Product.save(new Product(
  id = new ObjectId(),
  title = row(46),
  description = row(20),
  sku = row(0),
  price = if ( !row( 51 ).trim().equalsIgnoreCase( "" ) ) 
  BigDecimal( row( 51 ).trim().toDouble ) else BigDecimal( 0.0 ),
  image = imgList.empty,
  categories = List.empty,
  relatedProducts = List.empty
        ))
     }
     x=x+1
    }
  }
}
Following are the all imports that I have used in this controller.
import play.api.mvc._
import play.api.data.Forms._
import au.com.bytecode.opencsv.CSVReader
import java.io.FileReader
import java.io.InputStreamReader
import java.io.FileInputStream
import java.io.BufferedReader
import java.io.File
import play.Play
import com.mongodb.casbah.Imports._
import scala.collection.JavaConversions._
import scala.collection.mutable.ListBuffer
import scala.collection.immutable.Set
import com.novus.salat.global._
import com.novus.salat._
import com.mongodb.casbah.Imports.MongoDBObject
import com.mongodb.casbah.Imports.ObjectId
import com.novus.salat.grater
import models.salatctx._
I have to mention here that I'm using a sample csv file and it was really complex. above 'Product.save' part saves the actual MongoDB object to the DB and I have not added here model class for the 'Product'.

In Product model class I have Categories list, Images list and Related products list. In the above code I have set those lists to empty to keep this post simple.(of course it should come from the csv).

Also I have added if check to skip the first row of the document because it contains headers. I could not find any better way than this. Ok that is all and I hope you can get something from this. If you have better solution than this I would like to accept It. Thanks.

Comments

Unknown said…
hello,
i m new to mongo and play.
i m really impressed by your code, but if u add your model part in the code it will really helpful.

thanks,
Rahul kumar

Popular posts from this blog

Scala : not enough arguments for constructor : ()

not enough arguments for constructor : ( ) Here Im trying to create new Object from case class in scala, but I get this error withing my following code. def getTax(id: ObjectId): models.Tax = { models.Tax.findOne(MongoDBObject("_id" -> id)) .getOrElse(new models.Tax) // this line is generating error } It says that it cannot build a new Tax object without providing the constructor arguments. That is correct, If we want to buld an object we should provide constructor arguments. Just take a look at my Tax class. case class Tax( id: ObjectId = new ObjectId, var name: String, var description: String, var defaultFlag: Boolean, var rate: List[Rate] = List.empty ){ } I have specified default values for id field and rate field but what about name, description and defaultFlag field. There are no default values for them. So if we want to create and object like this 'new models.Tax' (which means without passing arguments to constructor), you should pr...

Way to Resolve 'no persistent classes found for query class'

Last few days I had experience about this issue. I was developing test application with Spring 3, Hibernate 3.3.1, Maven 3 and Jboss 5.0. I had a class called Employer and it was annotated. Here I have given sample. This is under the package hrm.com.domain. @Entity @Table(name="employer") public class Employer { @Id @Column(name="ID") @GeneratedValue private Integer id; My application was Spring MVC application and Im not going to post all the source code here. I had following configuration files. 1. hibernate.cfg.xml 2. spring-servlet.xml 3. web.xml Here is javax.persistence dependency in my POM.xml file. (I have only post here is important part) javax.persistence persistence-api 1.0 Then I built the application and copy the war file into jboss_home/server/default/deploy directory. Then Server is started. Server is started without any issue. Interested thing happened when I run the application in the web browser. Application needs to list down all the employ...