Java – what’s the correct way to send a file from REST web service to client

javajerseyjsonrestweb-services

I've just started to develop REST services, but I've come across a difficult situation: sending files from my REST service to my client. So far I've gotten the hang of how to send simple data types (strings, integers, etc) but sending a file is a different matter since there are so many file formats that I don't know where I should even begin. My REST service is made on Java and I'm using Jersey, I'm sending all the data using the JSON format.

I've read about base64 encoding, some people say it's a good technique, others say it isn't because of file size issues. What is the correct way? This is how a simple resource class in my project is looking:

import java.sql.SQLException;
import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.UriInfo;

import com.mx.ipn.escom.testerRest.dao.TemaDao;
import com.mx.ipn.escom.testerRest.modelo.Tema;

@Path("/temas")
public class TemaResource {

    @GET
    @Produces({MediaType.APPLICATION_JSON})
    public List<Tema> getTemas() throws SQLException{

        TemaDao temaDao = new TemaDao();        
        List<Tema> temas=temaDao.getTemas();
        temaDao.terminarSesion();

        return temas;
    }
}

I'm guessing the code for sending a file would be something like:

import java.sql.SQLException;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/resourceFiles")
public class FileResource {

    @GET
    @Produces({application/x-octet-stream})
    public File getFiles() throws SQLException{ //I'm not really sure what kind of data type I should return

        // Code for encoding the file or just send it in a data stream, I really don't know what should be done here

        return file;
    }
}

What kind of annotations should I use? I've seen some people recommend for a @GET using @Produces({application/x-octet-stream}), is that the correct way? The files I'm sending are specific ones so the client doesn't need to browse through the files. Can anyone guide me into how am I supposed to send the file? Should I encode it using base64 to send it as a JSON object? or the encoding isn't necessary to send it as a JSON object? Thanks for any help you may give.

Best Solution

I don't recommend encoding binary data in base64 and wrapping it in JSON. It will just needlessly increase the size of the response and slow things down.

Simply serve your file data using GET and application/octect-streamusing one of the factory methods of javax.ws.rs.core.Response (part of the JAX-RS API, so you're not locked into Jersey):

@GET
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile() {
  File file = ... // Initialize this to the File path you want to serve.
  return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
      .header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"" ) //optional
      .build();
}

If you don't have an actual File object, but an InputStream, Response.ok(entity, mediaType) should be able to handle that as well.