Chris Sum
Chris Sum

Reputation: 133

Export CSV File - JAX RS - REST - AJAX

I create a function to download a CSV File. I will use that to download simple reports. I got the error below on Netbeans using Wildfly and JAX RS

RESTEASY002005: Failed executing POST /reports/downloadCSV/: org.jboss.resteasy.core.NoMessageBodyWriterFoundFailure: Could not find MessageBodyWriter for response object of type: java.io.FileWriter of media type: application/octet-stream

Here is my Code:

Controller

Update on ParametersClass

@POST
@Path("/downloadCSV")
@Produces("application/octet-stream")
public Response downloadCSV(ParametersClass param) {
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    FileWriter fileWriter = null;
    Date date = new Date();
    try {
        fileWriter = new FileWriter("MainReport_"+dateFormat.format(date)+".csv");
        fileWriter.append(csvService.mainReport(dateFormat.parse(param.getStartDate()),dateFormat.parse(param.getEndDate())));
        fileWriter.flush();
        fileWriter.close();

        ResponseBuilder response = Response.ok((Object) fileWriter);  
        response.header("Content-Disposition","attachment; filename=\"MainReport_"+dateFormat.format(date)+".csv\"");  
        return response.build();  
    } catch (ParseException ex) {
        ex.printStackTrace();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
}

The csvService returns a String like:

 Column1,column2,column3
 cellInfo1,cellInfo2,cellInfo3
 ,cellInfo2,cellInfo3
 cellInfo1,,cellInfo3
 cellInfo1,cellInfo2,
 ,,cellInfo3

I tried using a different @Produces => @Produces('text/csv') , @Produces('application/octet-stream')

If I remove the Annotation @Produces I got the following error:

RESTEASY002010: Failed to execute: javax.ws.rs.NotSupportedException: RESTEASY003200: Could not find message body reader for type: class com.xo.CSVFile of content type: application/x-www-form-urlencoded;charset=UTF-8

AJAX

     var dateRange = new Object();
     dateRange.startDate = '2017-07-20';
     dateRange.endDate = '2017-08-10';
     $.ajax({
          type: 'POST',
          url: appPath + '/api/reports/downloadCSV/',
          data: JSON.stringify(dateRange),
          async:true,
          success: function(data) {
           }
     });

What I'm doing wrong ? Could you help to me please!

.

SOLUTION Thanks to @albert-bos

1st. Check the link in the solution from @albert-bos below.

2nd: Check this link too

3rd:

Controller:

@POST
@Path("/downloadCSV")
@Produces("text/csv")
public List<LinkedHashMap<String, String>> downloadCSV(ParametersClass param) {
    DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
    try {
        return csvService.mainReport(dateFormat.parse(param.getStartDate()),dateFormat.parse(param.getEndDate()));
    } catch (ParseException ex) {
        return null;
    }
}

MessageBodyWriter: I create a class called CSVMessageBodyWritter (check the link) but I adpated the method writeTo:

 @Override
 public void writeTo(Object t, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
      CsvSchema schema = null;
      CsvSchema.Builder schemaBuilder = CsvSchema.builder();
      if(t!=null){
           List<LinkedHashMap<String, String>> reportArray = (List<LinkedHashMap<String, String>>) t;
           LinkedHashMap<String, String> headers = reportArray.get(0);

           for (String col : headers.keySet()) {
                 schemaBuilder.addColumn(col);
           }
           schema = schemaBuilder.build().withLineSeparator("\r");
           CsvMapper mapper = new CsvMapper();
           mapper.writer(schema).writeValues(entityStream).writeAll(reportArray);
      }
 }

Upvotes: 4

Views: 12075

Answers (3)

Raja Ramachandran
Raja Ramachandran

Reputation: 456

JS

window.open("http://localhost:8080/xmlcompare-rs/xmlcompare/excelmisreport");

Rest

@GET
@Path("excelmisreport")
@Produces("application/vnd.ms-excel")
public Response getExcelReport() {
    ResponseBuilder response = Response.ok((Object) file);
    response.header("Content-Disposition",
        "attachment; filename=MISReport.xls");
    return response.build();
}

Upvotes: 2

Anatoly Zolotar
Anatoly Zolotar

Reputation: 1

I don't have enough rep so can't add a comment for the answer of Albert Bos.

There is a PITFALL with that solution: you can suddenly run into the problem when you get empty csv file even if it should have a data. It happens because of result of getSize method.

I'm not sure which version of JAX-RS is supposed to be used in that example (in the article), but accordingly to the jax-rs documentation, result of getSize is ignored for JAX-RS 2.0, but for JAX-RS 1.X it seems to be used and "return 0" makes downloaded file empty. Make sure you return "-1". I've encountered it when tried to implement csv export in JIRA rest plugin (I guess it's based on first version of JAX-RS).

Upvotes: 0

Albert Bos
Albert Bos

Reputation: 2062

JAX-RS only supports a few Content-Types by default (also depending on implementation), like XML and JSON.

The idea of JAX-RS is that it will convert an object to a certain type (e.g. XML or JSON). This is so you can re-use the same object for XML and JSON, without knowing the output in your Controller. Then If you want something different like CSV, you need to define your own BodyWriter, see example here: http://www.javaprocess.com/2015/08/a-simple-csv-messagebodywriter-for-jax.html

The problem here is that your controller is to specific for CSV and isn't very flexible. You could put your output of csvService into an object and let the BodyWriter convert it into CSV.

Upvotes: 3

Related Questions