6

I am trying to download a file from a REST service using JAX-RS. This is my code which invokes the download by sending a GET request:

private Response invokeDownload(String authToken, String url) {
    // Creates the HTTP client object and makes the HTTP request to the specified URL
    Client client = ClientBuilder.newClient();
    WebTarget target = client.target(url);

    // Sets the header and makes a GET request
    return target.request().header("X-Tableau-Auth", authToken).get();
}

However I am facing problems converting the Response into an actual File object. So what I did is the following:

public File downloadWorkbook(String authToken, String siteId, String workbookId, String savePath)
        throws IOException {
    String url = Operation.DOWNLOAD_WORKBOOK.getUrl(siteId, workbookId);
    Response response = invokeDownload(authToken, url);

    String output = response.readEntity(String.class);
    String filename; 
// some code to retrieve the filename from the headers
    Path path = Files.write(Paths.get(savePath + "/" + filename), output.getBytes());
    File file = path.toFile();
    return file;
}

The file which is created is not valid, I debugged the code and noticed that output contains a String like that (much larger):

PK ͢�F���� �[ Superstore.twb�ysI�7����ߡ���d�m3��f���

Looks like binary. Obviously there is something wrong with the code.

How do I get the HTTP response body as a string from the Response object?



Edit: Quote from the REST API reference about the HTTP response:

Response Body

One of the following, depending on the format of the workbook:

The workbook's content in .twb format (Content-Type: application/xml)
The workbook's content in .twbx format (Content-Type: application/octet-stream)

lenny
  • 2,978
  • 4
  • 23
  • 39
  • 4
    Your file doesn't seem to be a text file. Thus it is not a good idea to create a String from it. Rather use `response.getInputStream()` and write it to the file, e.g. using Apache Commons' `IOUtils.copy(in, out)`. – Sebastian S Jul 14 '15 at 18:29
  • This could be of some help http://stackoverflow.com/a/12251265/2294429 – Some guy Jul 14 '15 at 18:31
  • @SebastianS you should make that an answer. – Samuel Jul 14 '15 at 18:34
  • That output looks suspiciously like a zip file. – RealSkeptic Jul 14 '15 at 18:37
  • @Samuel I wasn't sure, if this is the sole reason (retrieved file could actually still be a text file). Added this as an answer now. – Sebastian S Jul 14 '15 at 18:42
  • @SebastianS even if it were a text file, your recommendation is best practice. It could be dangerous to load a potentially large file into memory. InputStreams are the way to go! – Samuel Jul 15 '15 at 17:39

2 Answers2

10

As you noticed yourself, you're dealing with binary data here. So you shouldn't create a String from your response. Better get the input stream and pipe it to your file.

Response response = invokeDownload(authToken, url);
InputStream in = response.readEntity(InputStream.class);
Path path = Paths.get(savePath, filename);
Files.copy(in, path);
Sebastian S
  • 4,420
  • 4
  • 34
  • 63
3

1) I assume by this point you're clear on the difference between "binary file" and "text file". And that you can only capture the latter into a "string".

2) Sebastian gave you excellent advice for capturing a binary file (+1, Sebastian!). VERY IMPORTANT: you should always set the MIME type (Content-Type: xxx/yyy)in cases like this. Here is another link that might be useful.

3) Finally, there are cases where you might WANT to treat "binary" data as text. This is how e-mail attachments work with SMTP (a text protocol). In these cases, you want to use Base64 Encoding. For example: JAX-RS | Download PDF from Base64 encoded data

Community
  • 1
  • 1
paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • 2) I changed the request like this: `target.request().accept(MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_XML).header("X-Tableau-Auth", authToken).get()` – lenny Jul 14 '15 at 23:09