Quelle est la bonne façon d’envoyer un fichier du service Web REST au client?

Je viens de commencer à développer des services REST, mais j’ai rencontré une situation difficile: envoyer des fichiers de mon service REST à mon client. Jusqu’à présent, j’ai appris comment envoyer des types de données simples (chaînes de caractères, nombres entiers, etc.), mais l’envoi de fichiers est différent car il y a tellement de formats de fichiers que je ne sais même pas où commencer. Mon service REST est fait en Java et j’utilise Jersey, j’envoie toutes les données au format JSON.

J’ai lu sur l’encodage base64, certaines personnes disent que c’est une bonne technique, d’autres disent que ce n’est pas à cause de problèmes de taille de fichier. Quelle est la bonne façon? Voici comment se présente une classe de ressources simple dans mon projet:

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

Je devine que le code pour envoyer un fichier serait quelque chose comme:

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

Quel genre d’annotations devrais-je utiliser? J’ai vu des gens recommander pour un @GET utilisant @Produces({application/x-octet-stream}) , est-ce la bonne façon? Les fichiers que j’envoie sont spécifiques, le client n’a donc pas besoin de parcourir les fichiers. Quelqu’un peut-il me guider dans la façon dont je suis censé envoyer le fichier? Dois-je l’encoder en utilisant base64 pour l’envoyer en tant qu’object JSON? ou l’encodage n’est pas nécessaire pour l’envoyer en tant qu’object JSON? Merci pour toute aide que vous pourriez donner.

Je ne recommande pas d’encoder les données binarys en base64 et de les envelopper dans JSON. Cela augmentera inutilement la taille de la réponse et ralentira les choses.

application/octect-stream simplement vos données de fichiers en utilisant GET et application/octect-stream utilisant l’une des méthodes d’usine de javax.ws.rs.core.Response (partie de l’API JAX-RS, vous n’êtes donc pas bloqué dans 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(); } 

Si vous ne disposez pas d’un object File réel, mais d’un InputStream , Response.ok(entity, mediaType) devrait également pouvoir le gérer.

Si vous voulez retourner un fichier à télécharger, spécialement si vous souhaitez l’intégrer à des librairies javascript de téléchargement de fichiers, le code ci-dessous devrait faire l’affaire:

 @GET @Path("/{key}") public Response download(@PathParam("key") Ssortingng 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(); } 

Modifiez l’adresse de l’ordinateur de localhost à l’adresse IP à laquelle votre client doit se connecter pour appeler le service mentionné ci-dessous.

Client pour appeler le service Web 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 Ssortingng 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); Ssortingng filename = filenameBodyPart.getEntityAs(Ssortingng.class); Ssortingng fileLength = fileLengthBodyPart .getEntityAs(Ssortingng.class); File streamedFile = fileBodyPart.getEntityAs(File.class); BufferedInputStream objBufferedInputStream = new BufferedInputStream( new FileInputStream(streamedFile)); byte[] bytes = new byte[objBufferedInputStream.available()]; objBufferedInputStream.read(bytes); Ssortingng 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(Ssortingng... args) { new DownloadFileClient(); } } 

Service au client de réponse:

 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 nécessaire:

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

WEB.XML:

 < ?xml version="1.0" encoding="UTF-8"?>  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   

Étant donné que vous utilisez JSON, Base64 l’encodera avant de l’envoyer via le réseau.

Si les fichiers sont volumineux, essayez de regarder BSON, ou un autre format mieux adapté aux transferts binarys.

Vous pouvez également compresser les fichiers, s’ils se compressent bien, avant de les encoder en base64.