diff --git a/doc/files.md b/doc/files.md
index 746ae52b5..b2247500c 100644
--- a/doc/files.md
+++ b/doc/files.md
@@ -1078,4 +1078,19 @@ BoxFile file = new BoxFile(api, "12345");
file.getRepresentationContent("[png?dimensions=1024x1024]", "1.png", output);
```
+Generating a representation for the selected file is an asynchronous operation and may take some time.
+Therefore, by default, the `getRepresentationContent` method periodically checks the status of the generated file and downloads it when it is ready.
+With the `maxRetries` parameter in [`getRepresentationContent(String representationHint, String assetPath, OutputStream output, int maxRetries)`][get-rep-content-overloaded], you can define
+the number of status checks for the generated file, which will be performed at intervals of 100 ms.
+
+If this number is exceeded, a `BoxApiException` will be thrown.
+
+```java
+FileOutputStream output = new FileOutputStream("/path/to/file.png");
+BoxFile file = new BoxFile(api, "12345");
+file.getRepresentationContent("[png?dimensions=1024x1024]", "1.png", output, 10);
+```
+
+
[get-rep-content]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxFile.html#getRepresentationContent-java.lang.String-java.lang.String-java.io.OutputStream-
+[get-rep-content-overloaded]: http://opensource.box.com/box-java-sdk/javadoc/com/box/sdk/BoxFile.html#getRepresentationContent-java.lang.String-java.lang.String-java.io.OutputStream-int-
diff --git a/src/main/java/com/box/sdk/BoxFile.java b/src/main/java/com/box/sdk/BoxFile.java
index 6faeec614..86edc6756 100644
--- a/src/main/java/com/box/sdk/BoxFile.java
+++ b/src/main/java/com/box/sdk/BoxFile.java
@@ -505,7 +505,23 @@ public void getRepresentationContent(String representationHint, OutputStream out
* @see X-Rep-Hints Header
*/
public void getRepresentationContent(String representationHint, String assetPath, OutputStream output) {
+ this.getRepresentationContent(representationHint, assetPath, output, Integer.MAX_VALUE);
+ }
+ /**
+ * Fetches the contents of a file representation with asset path and writes them to the provided output stream.
+ *
+ * @param representationHint the X-Rep-Hints query for the representation to fetch.
+ * @param assetPath the path of the asset for representations containing multiple files.
+ * @param output the output stream to write the contents to.
+ * @param maxRetries the maximum number of attempts to call the request for retrieving status information
+ * indicating whether the representation has been generated and is ready to fetch.
+ * If the number of attempts is exceeded, the method will throw a BoxApiException.
+ * @see X-Rep-Hints Header
+ */
+ public void getRepresentationContent(
+ String representationHint, String assetPath, OutputStream output, int maxRetries
+ ) {
List reps = this.getInfoWithRepresentations(representationHint).getRepresentations();
if (reps.size() < 1) {
throw new BoxAPIException("No matching representations found for requested '" + representationHint
@@ -523,16 +539,27 @@ public void getRepresentationContent(String representationHint, String assetPath
case "none":
String repContentURLString = null;
- while (repContentURLString == null) {
+ int attemptNumber = 0;
+ while (repContentURLString == null && attemptNumber < maxRetries) {
repContentURLString = this.pollRepInfo(representation.getInfo().getUrl());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
+ attemptNumber++;
+ }
+
+ if (repContentURLString != null) {
+ this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
+ } else {
+ throw new BoxAPIException(
+ "Representation did non have a success status allowing it to be retrieved after "
+ + maxRetries
+ + " attempts"
+ );
}
- this.makeRepresentationContentRequest(repContentURLString, assetPath, output);
break;
case "error":
throw new BoxAPIException("Representation had error status");
diff --git a/src/test/Fixtures/BoxFile/GetFileRepresentation200WithPending.json b/src/test/Fixtures/BoxFile/GetFileRepresentation200WithPending.json
new file mode 100644
index 000000000..ec1ade405
--- /dev/null
+++ b/src/test/Fixtures/BoxFile/GetFileRepresentation200WithPending.json
@@ -0,0 +1,17 @@
+{
+ "representation": "jpg",
+ "properties": {
+ "dimensions": "32x32",
+ "paged": false,
+ "thumb": true
+ },
+ "info": {
+ "url": "https://localhost:53621/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32"
+ },
+ "status": {
+ "state": "pending"
+ },
+ "content": {
+ "url_template": "https://localhost:53621/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/{+asset_path}"
+ }
+}
diff --git a/src/test/java/com/box/sdk/BoxFileTest.java b/src/test/java/com/box/sdk/BoxFileTest.java
index 7f67b0999..abfb3903f 100644
--- a/src/test/java/com/box/sdk/BoxFileTest.java
+++ b/src/test/java/com/box/sdk/BoxFileTest.java
@@ -15,6 +15,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import com.box.sdk.sharedlink.BoxSharedLinkRequest;
import com.eclipsesource.json.Json;
@@ -440,6 +441,91 @@ public void testGetThumbnailSucceeds() {
assertThat(output.toString(), equalTo("This is a JPG"));
}
+ @Test
+ public void testGetRepresentationContentThrowsWhenExceedingMaxRetries() {
+ final String fileID = "12345";
+ wireMockRule.stubFor(
+ WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
+ .withQueryParam("fields", WireMock.equalTo("representations"))
+ .willReturn(WireMock.aResponse()
+ .withHeader("Content-Type", APPLICATION_JSON)
+ .withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
+ .withStatus(200))
+ );
+ wireMockRule.stubFor(
+ WireMock.get(WireMock.urlPathEqualTo(
+ "/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
+ )
+ .willReturn(WireMock.aResponse()
+ .withHeader("Content-Type", APPLICATION_JSON)
+ .withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
+ .withStatus(200))
+ );
+
+ try {
+ BoxFile file = new BoxFile(this.api, fileID);
+ OutputStream output = new ByteArrayOutputStream();
+ file.getRepresentationContent("[jpg?dimensions=32x32]", "", output, 5);
+ fail("getRepresentationContent did not fail with BoxAPIException due to pending status");
+ assertThat(output.toString(), equalTo("This is a JPG"));
+ } catch (BoxAPIException apiException) {
+ assertEquals(
+ apiException.getMessage(),
+ "Representation did non have a success status allowing it to be retrieved after 5 attempts"
+ );
+ }
+ }
+
+ @Test
+ public void testGetRepresentationContentSuccess() {
+ final String fileID = "12345";
+ wireMockRule.stubFor(
+ WireMock.get(WireMock.urlPathEqualTo("/2.0/files/" + fileID))
+ .withQueryParam("fields", WireMock.equalTo("representations"))
+ .willReturn(WireMock.aResponse()
+ .withHeader("Content-Type", APPLICATION_JSON)
+ .withBody(getFixture("BoxFile/GetFileRepresentations200", wireMockRule.httpsPort()))
+ .withStatus(200))
+ );
+ wireMockRule.stubFor(
+ WireMock.get(WireMock.urlPathEqualTo(
+ "/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
+ )
+ .inScenario("Get file representation status info")
+ .willSetStateTo("pending status")
+ .willReturn(WireMock.aResponse()
+ .withHeader("Content-Type", APPLICATION_JSON)
+ .withBody(getFixture("BoxFile/GetFileRepresentation200WithPending", wireMockRule.httpsPort()))
+ .withStatus(200))
+ );
+ wireMockRule.stubFor(
+ WireMock.get(WireMock.urlPathEqualTo(
+ "/2.0/internal_files/12345/versions/1116420931563/representations/jpg_thumb_32x32")
+ )
+ .inScenario("Get file representation status info")
+ .whenScenarioStateIs("pending status")
+ .willReturn(WireMock.aResponse()
+ .withHeader("Content-Type", APPLICATION_JSON)
+ .withBody(getFixture("BoxFile/GetFileRepresentation200", wireMockRule.httpsPort()))
+ .withStatus(200))
+ );
+
+ wireMockRule.stubFor(
+ WireMock.get(WireMock.urlPathEqualTo(
+ "/2.0/internal_files/1030335435441/versions/1116437417841/representations/jpg_thumb_32x32/content/"
+ ))
+ .willReturn(WireMock.aResponse()
+ .withHeader("Content-Type", "image/jpg")
+ .withBody("This is a JPG")
+ .withStatus(200))
+ );
+
+ BoxFile file = new BoxFile(this.api, fileID);
+ OutputStream output = new ByteArrayOutputStream();
+ file.getRepresentationContent("[jpg?dimensions=32x32]", output);
+ assertThat(output.toString(), equalTo("This is a JPG"));
+ }
+
@Test
public void testDeletePreviousFileVersionSucceeds() {
final String versionID = "12345";