web-dev-qa-db-fra.com

Quelle est la méthode correcte pour envoyer un fichier du service Web REST au client?

Je viens tout juste de commencer à développer les services REST, mais je suis tombé sur une situation difficile: envoyer des fichiers de mon service REST à mon client. Jusqu'ici, j'ai compris comment envoyer des types de données simples (chaînes, entiers, etc.), mais envoyer un fichier est une autre affaire, car il y a tellement de formats de fichiers que je ne sais pas par où commencer. Mon service REST est effectué sur Java et j'utilise Jersey. J'envoie toutes les données au format JSON.

J'ai lu sur le codage 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 manière? Voici à quoi ressemble 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<Tema> getTemas() throws SQLException{

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

        return temas;
    }
}

Je suppose 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 type d'annotations dois-je utiliser? J'ai vu certaines personnes recommander un @GET en utilisant @Produces({application/x-octet-stream}), est-ce la bonne façon? Les fichiers que j'envoie sont spécifiques et le client n'a pas besoin de les parcourir. Quelqu'un peut-il m'indiquer comment je suis censé envoyer le fichier? Devrais-je l'encoder en utilisant base64 pour l'envoyer en tant qu'objet JSON? ou l'encodage n'est pas nécessaire pour l'envoyer en tant qu'objet JSON? Merci pour toute aide que vous pourriez donner.

91
Uriel

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

Servez simplement vos données de fichier en utilisant GET et _application/octect-stream_ en utilisant l'une des méthodes d'usine de javax.ws.rs.core.Response (partie de l'API JAX-RS, pour ne pas être 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 n'avez pas d'objet File réel, mais un objet InputStream, Response.ok(entity, mediaType) , vous devriez également pouvoir le gérer.

122
Philipp Reichart

Si vous souhaitez renvoyer un fichier à télécharger, en particulier si vous souhaitez intégrer certaines bibliothèques javascript de téléchargement/téléchargement de fichier, le code ci-dessous devrait alors être utile:

@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();
}
5
john 4d5

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

Le client doit appeler REST webservice:

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<BodyPart> 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();
    }
}

Service au client de la 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"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://Java.Sun.com/xml/ns/javaee" xmlns:web="http://Java.Sun.com/xml/ns/javaee/web-app_2_5.xsd"
    xsi:schemaLocation="http://Java.Sun.com/xml/ns/javaee http://Java.Sun.com/xml/ns/javaee/web-app_2_5.xsd"
    id="WebApp_ID" version="2.5">
    <display-name>DownloadFileDemo</display-name>
    <servlet>
        <display-name>JAX-RS REST Servlet</display-name>
        <servlet-name>JAX-RS REST Servlet</servlet-name>
        <servlet-class>com.Sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param>
             <param-name>com.Sun.jersey.config.property.packages</param-name> 
             <param-value>in.india.service.downloadfiledemo</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>JAX-RS REST Servlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
5