What's the Correct Way to Send a File from Rest Web Service to Client

what's the correct way to send a file from REST web service to client?

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.

How can I send a File over a REST API?

I'd recommend you look into RestSharp
http://restsharp.org/

The RestSharp library has methods for posting files to a REST service. (RestRequst.AddFile()). I believe on the server-side this will translate into an encoded string into the body, with the content-type in the header specifying the file type.

I've also seen it done by converting a stream to a base-64 string, and transferring that as one of the properties of the serialized json/xml object. Especially if you can set size limits and want to include file meta-data in the request as part of the same object, this works really well.

It really depends how large your files are though. If they are very large, you need to consider streaming, of which the nuances of that is covered in this SO post pretty thoroughly: How do streaming resources fit within the RESTful paradigm?

What is the correct way of returning a file in a Rest webservice request?

All three are valid, depending on the client's needs, so why not make it the client's choice?

Let's say it's an image/jpeg file:

If the client sent the Accept: image/jpeg header, you return the file as a normal download.

If the client sent the Accept: image/jpeg header and the Accept-Encoding: base64, you return it as a normal download, Base64 encoded.

If the client sends the Accept: text/uri-list header, you return a plain text response with the URI for the image stored somewhere else.

There are no standard mimetypes to return base64 or links inside json, but you can use some standard of your own and create a vnd mimetype, as long as you document it.

Posting a File and Associated Data to a RESTful WebService preferably as JSON

I asked a similar question here:

How do I upload a file with metadata using a REST web service?

You basically have three choices:

  1. Base64 encode the file, at the expense of increasing the data size by around 33%, and add processing overhead in both the server and the client for encoding/decoding.
  2. Send the file first in a multipart/form-data POST, and return an ID to the client. The client then sends the metadata with the ID, and the server re-associates the file and the metadata.
  3. Send the metadata first, and return an ID to the client. The client then sends the file with the ID, and the server re-associates the file and the metadata.

Send File as a parameter to a REST Service, from a client?

Assuming you are using Jersey on both the client and server side, here is some code that you can extend:

Server side:

@POST
@Path("/")
@Produces(MediaType.TEXT_PLAIN)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(final MimeMultipart file) {
if (file == null)
return Response.status(Status.BAD_REQUEST)
.entity("Must supply a valid file").build();

try {
for (int i = 0; i < file.getCount(); i++) {
System.out.println("Body Part: " + file.getBodyPart(i));
}
return Response.ok("Done").build();
} catch (final Exception e) {
return Response.status(Status.INTERNAL_SERVER_ERROR).entity(e)
.build();
}
}

The above code implements a resource method that accepts POST's of multipart (file) data. It also illustrates how you can iterate through all the individual body parts in the incoming (multipart) request.

Client:

final ClientConfig config = new DefaultClientConfig();
final Client client = Client.create(config);

final WebResource resource = client.resource(ENDPOINT_URL);

final MimeMultipart request = new MimeMultipart();
request.addBodyPart(new MimeBodyPart(new FileInputStream(new File(
fileName))));

final String response = resource
.entity(request, "multipart/form-data")
.accept("text/plain")
.post(String.class);

The above code simply attaches a file to a multipart request, and fires the request off to the server. For both client and server side code there is a reliance on the Jersey and JavaMail libraries. If you are using Maven, these can be pulled down with ease, with the following dependencies:

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.17</version>
</dependency>

<dependency> <!-- only on server side -->
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-server</artifactId>
<version>1.14</version>
</dependency>

<dependency> <!-- only on client side -->
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.17</version>
</dependency>

<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.17</version>
</dependency>

<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.6</version>
</dependency>

Adjust the dependency versions as required

How to send a local file through a REST service?

On your server, you will have to have a service with a method that accepts a string input, which you call with the file path from the client application.

You then read/copy/whichever the file from that location, on your server via normal file IO methods.

An example of how to do this you can find below.
The definition of ServerPleaseFetchThisFile naturally depends on what kind of webservice this would be, WCF or IIS web service or self made web service.

public bool ServerPleaseFetchThisFile(string targetPath)
{
// targetPath should enter from the client in format of \\Hostname\Path\to\the\file.txt
return DoSomethingWithAFile(targetPath);
}

private bool DoSomethingWithAFile(string targetFile)
{
bool success = false;

if (string.IsNullOrWhiteSpace(targetFile))
{
throw new ArgumentNullException("targetFile", "The supplied target file is not a valid input.");
}

if (!File.Exists(targetFile))
{
throw new ArgumentNullException("targetFile", "The supplied target file is not a valid file location.");
}

try
{
using (FileStream targetStream = new FileStream(targetFile, FileMode.Open, FileAccess.Read))
{
// Do something with targetStream
success = true;
}
}
catch (SecurityException se)
{
throw new Exception("Security Exception!", se);
// Do something due to indicate Security Exception to the file
// success = false;
}
catch (UnauthorizedAccessException uae)
{
throw new Exception("Unathorized Access!", uae);
// Do something due to indicate Unauthorized Access to the file
// success = false;
}

return success;
}

file downloading in restful web services

Its the better way and easy way for file dowload.

private static final String FILE_PATH = "d:\\Test2.zip";
@GET
@Path("/get")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile() {
File file = new File(FILE_PATH);
ResponseBuilder response = Response.ok((Object) file);
response.header("Content-Disposition", "attachment; filename=newfile.zip");
return response.build();

}

For your code as you asked:

@GET
@Path("/helloWorldZip")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public StreamingOutput helloWorldZip() throws Exception {
return new StreamingOutput(){
@Override
public void write(OutputStream arg0) throws IOException, WebApplicationException {
// TODO Auto-generated method stub
BufferedOutputStream bus = new BufferedOutputStream(arg0);
try {
//ByteArrayInputStream reader = (ByteArrayInputStream) Thread.currentThread().getContextClassLoader().getResourceAsStream();
//byte[] input = new byte[2048];
java.net.URL uri = Thread.currentThread().getContextClassLoader().getResource("");
File file = new File("D:\\Test1.zip");
FileInputStream fizip = new FileInputStream(file);
byte[] buffer2 = IOUtils.toByteArray(fizip);
bus.write(buffer2);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}

Send file from rest web service

Since this file is likely to be just one part of a larger response, base64 is usually the best option.



Related Topics



Leave a reply



Submit