Qual é a maneira correta de enviar um arquivo do serviço web REST para o cliente?

Acabei de começar a desenvolver serviços REST, mas me deparei com uma situação difícil: enviar arquivos do meu serviço REST para meu cliente. Até agora eu peguei o jeito de enviar tipos de dados simples (strings, inteiros, etc), mas enviar um arquivo é um assunto diferente, já que existem tantos formatos de arquivos que eu não sei onde devo começar. Meu serviço REST é feito em Java e estou usando Jersey, estou enviando todos os dados usando o formato JSON.

Eu li sobre codificação de base64, algumas pessoas dizem que é uma boa técnica, outros dizem que não é por causa de problemas de tamanho de arquivo. Qual é o caminho correto? É assim que uma simples class de resources no meu projeto está procurando:

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 getTemas() throws SQLException{ TemaDao temaDao = new TemaDao(); List temas=temaDao.getTemas(); temaDao.terminarSesion(); return temas; } } 

Eu estou supondo que o código para enviar um arquivo seria algo como:

 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; } } 

Que tipo de annotations devo usar? Eu já vi algumas pessoas recomendarem para um @GET usando @Produces({application/x-octet-stream}) , essa é a maneira correta? Os arquivos que estou enviando são específicos para que o cliente não precise navegar pelos arquivos. Alguém pode me orientar sobre como devo enviar o arquivo? Devo codificá-lo usando base64 para enviá-lo como um object JSON? ou a codificação não é necessária para enviá-lo como um object JSON? Obrigado por qualquer ajuda que você possa dar.

Eu não recomendo codificar dados binários em base64 e envolvê-los em JSON. Isso aumentará desnecessariamente o tamanho da resposta e atrasará as coisas.

Simplesmente sirva seus dados de arquivo usando GET e application/octect-stream usando um dos methods de fábrica de javax.ws.rs.core.Response (parte da API do JAX-RS, então você não está preso em 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(); } 

Se você não tiver um object File real, mas um InputStream , Response.ok(entity, mediaType) deverá ser capaz de lidar com isso.

Se você quiser retornar um arquivo para ser baixado, especialmente se você quiser integrar com algumas bibliotecas javascript de upload / download de arquivos, então o código abaixo deve fazer o trabalho:

 @GET @Path("/{key}") public Response download(@PathParam("key") String key, @Context HttpServletResponse response) throws IOException { try { //Get your File or Object from wherever you want... //you can use the key parameter to indentify your file //otherwise it can be removed //let's say your file is called "object" response.setContentLength((int) object.getContentLength()); response.setHeader("Content-Disposition", "attachment; filename=" + object.getName()); ServletOutputStream outStream = response.getOutputStream(); byte[] bbuf = new byte[(int) object.getContentLength() + 1024]; DataInputStream in = new DataInputStream( object.getDataInputStream()); int length = 0; while ((in != null) && ((length = in.read(bbuf)) != -1)) { outStream.write(bbuf, 0, length); } in.close(); outStream.flush(); } catch (S3ServiceException e) { e.printStackTrace(); } catch (ServiceException e) { e.printStackTrace(); } return Response.ok().build(); } 

Altere o endereço da máquina de localhost para o endereço IP que você deseja que seu cliente conecte para chamar o serviço mencionado abaixo.

Cliente para chamar webservice REST:

 package in.india.client.downloadfiledemo; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientHandlerException; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.UniformInterfaceException; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.multipart.BodyPart; import com.sun.jersey.multipart.MultiPart; public class DownloadFileClient { private static final String BASE_URI = "http://localhost:8080/DownloadFileDemo/services/downloadfile"; public DownloadFileClient() { try { Client client = Client.create(); WebResource objWebResource = client.resource(BASE_URI); ClientResponse response = objWebResource.path("/") .type(MediaType.TEXT_HTML).get(ClientResponse.class); System.out.println("response : " + response); if (response.getStatus() == Status.OK.getStatusCode() && response.hasEntity()) { MultiPart objMultiPart = response.getEntity(MultiPart.class); java.util.List listBodyPart = objMultiPart .getBodyParts(); BodyPart filenameBodyPart = listBodyPart.get(0); BodyPart fileLengthBodyPart = listBodyPart.get(1); BodyPart fileBodyPart = listBodyPart.get(2); String filename = filenameBodyPart.getEntityAs(String.class); String fileLength = fileLengthBodyPart .getEntityAs(String.class); File streamedFile = fileBodyPart.getEntityAs(File.class); BufferedInputStream objBufferedInputStream = new BufferedInputStream( new FileInputStream(streamedFile)); byte[] bytes = new byte[objBufferedInputStream.available()]; objBufferedInputStream.read(bytes); String outFileName = "D:/" + filename; System.out.println("File name is : " + filename + " and length is : " + fileLength); FileOutputStream objFileOutputStream = new FileOutputStream( outFileName); objFileOutputStream.write(bytes); objFileOutputStream.close(); objBufferedInputStream.close(); File receivedFile = new File(outFileName); System.out.print("Is the file size is same? :\t"); System.out.println(Long.parseLong(fileLength) == receivedFile .length()); } } catch (UniformInterfaceException e) { e.printStackTrace(); } catch (ClientHandlerException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void main(String... args) { new DownloadFileClient(); } } 

Serviço ao cliente de resposta:

 package in.india.service.downloadfiledemo; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.sun.jersey.multipart.MultiPart; @Path("downloadfile") @Produces("multipart/mixed") public class DownloadFileResource { @GET public Response getFile() { java.io.File objFile = new java.io.File( "D:/DanGilbert_2004-480p-en.mp4"); MultiPart objMultiPart = new MultiPart(); objMultiPart.type(new MediaType("multipart", "mixed")); objMultiPart .bodyPart(objFile.getName(), new MediaType("text", "plain")); objMultiPart.bodyPart("" + objFile.length(), new MediaType("text", "plain")); objMultiPart.bodyPart(objFile, new MediaType("multipart", "mixed")); return Response.ok(objMultiPart).build(); } } 

JAR necessário:

 jersey-bundle-1.14.jar jersey-multipart-1.14.jar mimepull.jar 

WEB.XML:

   DownloadFileDemo  JAX-RS REST Servlet JAX-RS REST Servlet com.sun.jersey.spi.container.servlet.ServletContainer  com.sun.jersey.config.property.packages in.india.service.downloadfiledemo  1   JAX-RS REST Servlet /services/*   index.jsp   
 package com.mastercard.dispute.pro.openapi.casefiling.restservice.impl; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import javax.ws.rs.core.MediaType; public class DownloadFileClient { // http://localhost:8080/RESTfulExample/json/product/get public static void main(String[] args) { FileOutputStream out = null; try { URL url = new URL( "http://localhost:8080/ProService/json/claims/document/case/10000016?userId=n000027&&format=ORIGINAL"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", MediaType.MULTIPART_FORM_DATA); conn.setRequestProperty("system_id", "op"); conn.setRequestProperty("source", "s0001"); conn.setRequestProperty("user_id", "n000027"); if (conn.getResponseCode() != 200) { throw new RuntimeException("Failed : HTTP error code : " + conn.getResponseCode()); } /* * BufferedReader br = new BufferedReader(new InputStreamReader( * (conn.getInputStream()))); */ InputStream in = conn.getInputStream(); out = new FileOutputStream( "C:\\Users\\E077106\\Desktop\\upload_15_05_2017\\sunil.zip"); copy(in, out, 1024); out.close(); String output; System.out.println("Output from Server .... \n"); /* * while ((output = br.readLine()) != null) { * System.out.println(output); } */ conn.disconnect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } public static void copy(InputStream input, OutputStream output, int bufferSize) throws IOException { byte[] buf = new byte[bufferSize]; int n = input.read(buf); while (n >= 0) { output.write(buf, 0, n); n = input.read(buf); } output.flush(); } } 

Desde que você está usando JSON, gostaria de codificá-lo antes de enviá-lo através do fio.

Se os arquivos forem grandes, tente ver o BSON ou algum outro formato que seja melhor com transferências binárias.

Você também pode compactar os arquivos, se eles compactarem bem, antes de base64 codificá-los.