REST वेब सेवा से क्लाइंट को फ़ाइल भेजने का सही तरीका क्या है?


103

मैंने अभी आरईएसटी सेवाओं को विकसित करना शुरू किया है, लेकिन मैं एक मुश्किल स्थिति में आया हूं: मेरे आरईएस सेवा से अपने क्लाइंट को फाइलें भेजना। अब तक मैं कैसे सरल डेटा प्रकार (तार, पूर्णांक, आदि) भेजने के लिए लटका दिया है, लेकिन एक फ़ाइल भेजना एक अलग मामला है क्योंकि बहुत सारे फ़ाइल स्वरूप हैं जो मुझे नहीं पता कि मुझे कहां से भी शुरू करना चाहिए। मेरी REST सेवा जावा पर बनी है और मैं जर्सी का उपयोग कर रहा हूं, मैं JSON प्रारूप का उपयोग करके सभी डेटा भेज रहा हूं।

मैंने बेस 64 एनकोडिंग के बारे में पढ़ा है, कुछ लोगों का कहना है कि यह एक अच्छी तकनीक है, दूसरों का कहना है कि यह फ़ाइल आकार के मुद्दों के कारण नहीं है। सही तरीका क्या है? इस तरह मेरी परियोजना में एक साधारण संसाधन वर्ग दिख रहा है:

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

मैं अनुमान लगा रहा हूं कि फाइल भेजने के लिए कोड कुछ इस तरह होगा:

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

मुझे किस प्रकार के एनोटेशन का उपयोग करना चाहिए? मैंने देखा है कि कुछ लोग @GETउपयोग के लिए सलाह देते हैं @Produces({application/x-octet-stream}), क्या यह सही तरीका है? मैं जो फाइलें भेज रहा हूं वे विशिष्ट हैं इसलिए क्लाइंट को फाइलों के माध्यम से ब्राउज़ करने की आवश्यकता नहीं है। क्या कोई मुझे गाइड कर सकता है कि मुझे फाइल कैसे भेजनी है? क्या मुझे इसे J64 ऑब्जेक्ट के रूप में भेजने के लिए base64 का उपयोग कर सांकेतिक शब्दों में बदलना चाहिए? या एन्कोडिंग JSON ऑब्जेक्ट के रूप में भेजने के लिए आवश्यक नहीं है? किसी भी मदद के लिए धन्यवाद आप दे सकते हैं।


क्या आपके पास java.io.Fileअपने सर्वर पर वास्तविक (या फ़ाइल पथ) है या डेटा किसी अन्य स्रोत से आ रहा है, जैसे डेटाबेस, वेब सेवा, मेथड कॉल रिटर्निंग ए InputStream?
फिलिप रेचर्ट

जवाबों:


138

मैं आधारभूत बाइनरी डेटा को एन्कोडिंग और JSON में लपेटने की अनुशंसा नहीं करता। यह सिर्फ अनावश्यक रूप से प्रतिक्रिया के आकार को बढ़ाएगा और चीजों को धीमा कर देगा।

बस GET का application/octect-streamउपयोग करके javax.ws.rs.core.Response(JAX-RS API का एक हिस्सा, ताकि आप जर्सी में बंद न हों) का उपयोग करके अपने फ़ाइल डेटा की सेवा करें :

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

यदि आपके पास कोई वास्तविक Fileवस्तु नहीं है , लेकिन ए InputStream, Response.ok(entity, mediaType)को भी संभालना चाहिए।


धन्यवाद, यह बहुत अच्छा काम किया, लेकिन क्या होगा अगर मैं एक संपूर्ण फ़ोल्डर संरचना का उपभोग करना चाहता हूं? मैं इस तरह से कुछ सोच रहा था, क्योंकि मैं क्लाइंट पर विभिन्न फाइलें प्राप्त कर रहा हूं, मुझे HttpResponse की इकाई प्रतिक्रिया का इलाज कैसे करना चाहिए?
उरईल

4
ZipOutputStreamएक StreamingOutputसे लौटने के साथ-साथ देख लें getFile()। इस तरह से आपको एक जाना-पहचाना मल्टी-फाइल फॉर्मेट मिलता है, जिसे ज्यादातर क्लाइंट्स आसानी से पढ़ सकते हैं। संपीड़न का उपयोग केवल तभी करें जब यह आपके डेटा के लिए समझ में आता है, अर्थात जेपीईजी जैसी पूर्व-संकुचित फ़ाइलों के लिए नहीं। क्लाइंट की तरफ, ZipInputStreamप्रतिक्रिया को पार्स करने के लिए है।
फिलिप रीचर्ट

1
यह मदद कर सकता है: stackoverflow.com/questions/10100936/…
बेसिल डिसूजा

फ़ाइल बाइनरी डेटा के साथ प्रतिक्रिया में फ़ाइल का मेटाडेटा जोड़ने का कोई तरीका है?
abhig

आप हमेशा प्रतिक्रिया में अधिक हेडर जोड़ सकते हैं। यदि यह पर्याप्त नहीं है तो आपको इसे ऑक्टेट स्ट्रीम में बदलना होगा, अर्थात एक कंटेनर प्रारूप की सेवा करें जिसमें मेटाडेटा और आपके द्वारा वांछित फ़ाइल दोनों हों।
फिलिप रीचर्ट

6

यदि आप डाउनलोड की जाने वाली फ़ाइल को वापस करना चाहते हैं, विशेष रूप से यदि आप फ़ाइल अपलोड / डाउनलोड के कुछ जावास्क्रिप्ट लिबास के साथ एकीकृत करना चाहते हैं, तो कोड बेलो को काम करना चाहिए:

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

3

लोकलहोस्ट से आईपी पते के लिए मशीन का पता बदलें जो आप चाहते हैं कि आपका ग्राहक नीचे दी गई सेवा से कॉल करने के लिए कनेक्ट हो।

ग्राहक कॉल करने के लिए अन्य वेब सेवा:

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

ग्राहक की प्रतिक्रिया के लिए सेवा:

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

    }
}

जार की जरूरत:

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>

-2

चूंकि आप JSON का उपयोग कर रहे हैं, मैं इसे वायर पर भेजने से पहले Base64 को एनकोड कर दूंगा।

यदि फाइलें बड़ी हैं, तो BSON या कुछ अन्य प्रारूप को देखने की कोशिश करें जो बाइनरी ट्रांसफर के साथ बेहतर हैं।

बेस 64 एनकोडिंग से पहले, यदि आप अच्छी तरह से सेक करते हैं, तो आप फ़ाइलों को ज़िप कर सकते हैं।


मैं उन्हें पूरे फ़ाइल आकार कारण के लिए भेजने से पहले उन्हें ज़िप करने की योजना बना रहा था, लेकिन अगर मैं इसे आधार बनाता हूं, तो मेरे एनोटोट में क्या होना चाहिए @Produces?
ऊरीएल

JSON कल्पना के अनुसार आवेदन / json, चाहे आप इसे में डाल दिया। ( etf.org/rfc/rfc4627.txt?number=4627 ) ध्यान रखें कि आधार 64 एन्कोडेड फ़ाइल अभी भी JSON टैग्स के अंदर होनी चाहिए
LarsK

3
Base64 में बाइनरी डेटा को एन्कोडिंग करने और फिर JSON में लपेटने से कोई लाभ नहीं है। यह सिर्फ अनावश्यक रूप से प्रतिक्रिया के आकार को बढ़ाएगा और चीजों को धीमा कर देगा।
फिलिप रीचर्ट सेप
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.