001package com.box.sdk; 002 003import java.io.ByteArrayOutputStream; 004import java.io.IOException; 005import java.io.InputStream; 006import java.io.OutputStream; 007import java.net.MalformedURLException; 008import java.net.URL; 009import java.util.ArrayList; 010import java.util.Arrays; 011import java.util.Collection; 012import java.util.Date; 013import java.util.EnumSet; 014import java.util.HashSet; 015import java.util.List; 016import java.util.Map; 017import java.util.Set; 018import java.util.concurrent.TimeUnit; 019 020import com.box.sdk.http.HttpMethod; 021import com.box.sdk.internal.utils.Parsers; 022import com.eclipsesource.json.JsonArray; 023import com.eclipsesource.json.JsonObject; 024import com.eclipsesource.json.JsonValue; 025 026 027/** 028 * Represents an individual file on Box. This class can be used to download a file's contents, upload new versions, and 029 * perform other common file operations (move, copy, delete, etc.). 030 * 031 * <p>Unless otherwise noted, the methods in this class can throw an unchecked {@link BoxAPIException} (unchecked 032 * meaning that the compiler won't force you to handle it) if an error occurs. If you wish to implement custom error 033 * handling for errors related to the Box REST API, you should capture this exception explicitly. 034 */ 035@BoxResourceType("file") 036public class BoxFile extends BoxItem { 037 038 /** 039 * An array of all possible file fields that can be requested when calling {@link #getInfo()}. 040 */ 041 public static final String[] ALL_FIELDS = {"type", "id", "sequence_id", "etag", "sha1", "name", 042 "description", "size", "path_collection", "created_at", "modified_at", 043 "trashed_at", "purged_at", "content_created_at", "content_modified_at", 044 "created_by", "modified_by", "owned_by", "shared_link", "parent", 045 "item_status", "version_number", "comment_count", "permissions", "tags", 046 "lock", "extension", "is_package", "file_version", "collections", 047 "watermark_info", "metadata", "representations"}; 048 049 /** 050 * Used to specify what filetype to request for a file thumbnail. 051 */ 052 public enum ThumbnailFileType { 053 /** 054 * PNG image format. 055 */ 056 PNG, 057 058 /** 059 * JPG image format. 060 */ 061 JPG 062 } 063 064 /** 065 * File URL Template. 066 */ 067 public static final URLTemplate FILE_URL_TEMPLATE = new URLTemplate("files/%s"); 068 /** 069 * Content URL Template. 070 */ 071 public static final URLTemplate CONTENT_URL_TEMPLATE = new URLTemplate("files/%s/content"); 072 /** 073 * Versions URL Template. 074 */ 075 public static final URLTemplate VERSIONS_URL_TEMPLATE = new URLTemplate("files/%s/versions"); 076 /** 077 * Copy URL Template. 078 */ 079 public static final URLTemplate COPY_URL_TEMPLATE = new URLTemplate("files/%s/copy"); 080 /** 081 * Add Comment URL Template. 082 */ 083 public static final URLTemplate ADD_COMMENT_URL_TEMPLATE = new URLTemplate("comments"); 084 /** 085 * Get Comments URL Template. 086 */ 087 public static final URLTemplate GET_COMMENTS_URL_TEMPLATE = new URLTemplate("files/%s/comments"); 088 /** 089 * Metadata URL Template. 090 */ 091 public static final URLTemplate METADATA_URL_TEMPLATE = new URLTemplate("files/%s/metadata/%s/%s"); 092 /** 093 * Add Task URL Template. 094 */ 095 public static final URLTemplate ADD_TASK_URL_TEMPLATE = new URLTemplate("tasks"); 096 /** 097 * Get Tasks URL Template. 098 */ 099 public static final URLTemplate GET_TASKS_URL_TEMPLATE = new URLTemplate("files/%s/tasks"); 100 /** 101 * Get Thumbnail PNG Template. 102 */ 103 public static final URLTemplate GET_THUMBNAIL_PNG_TEMPLATE = new URLTemplate("files/%s/thumbnail.png"); 104 /** 105 * Get Thumbnail JPG Template. 106 */ 107 public static final URLTemplate GET_THUMBNAIL_JPG_TEMPLATE = new URLTemplate("files/%s/thumbnail.jpg"); 108 /** 109 * Upload Session URL Template. 110 */ 111 public static final URLTemplate UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/%s/upload_sessions"); 112 /** 113 * Upload Session Status URL Template. 114 */ 115 public static final URLTemplate UPLOAD_SESSION_STATUS_URL_TEMPLATE = new URLTemplate( 116 "files/upload_sessions/%s/status"); 117 /** 118 * Abort Upload Session URL Template. 119 */ 120 public static final URLTemplate ABORT_UPLOAD_SESSION_URL_TEMPLATE = new URLTemplate("files/upload_sessions/%s"); 121 /** 122 * Add Collaborations URL Template. 123 */ 124 public static final URLTemplate ADD_COLLABORATION_URL = new URLTemplate("collaborations"); 125 /** 126 * Get All File Collaborations URL Template. 127 */ 128 public static final URLTemplate GET_ALL_FILE_COLLABORATIONS_URL = new URLTemplate("files/%s/collaborations"); 129 private static final int BUFFER_SIZE = 8192; 130 private static final int GET_COLLABORATORS_PAGE_SIZE = 1000; 131 132 /** 133 * Constructs a BoxFile for a file with a given ID. 134 * 135 * @param api the API connection to be used by the file. 136 * @param id the ID of the file. 137 */ 138 public BoxFile(BoxAPIConnection api, String id) { 139 super(api, id); 140 } 141 142 /** 143 * {@inheritDoc} 144 */ 145 @Override 146 protected URL getItemURL() { 147 return FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 148 } 149 150 @Override 151 public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate, 152 BoxSharedLink.Permissions permissions) { 153 154 BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions); 155 Info info = new Info(); 156 info.setSharedLink(sharedLink); 157 158 this.updateInfo(info); 159 return info.getSharedLink(); 160 } 161 162 /** 163 * Creates new SharedLink for a BoxFile with a password. 164 * 165 * @param access The access level of the shared link. 166 * @param unshareDate A specified date to unshare the Box file. 167 * @param permissions The permissions to set on the shared link for the Box file. 168 * @param password Password set on the shared link to give access to the Box file. 169 * @return information about the newly created shared link. 170 */ 171 public BoxSharedLink createSharedLink(BoxSharedLink.Access access, Date unshareDate, 172 BoxSharedLink.Permissions permissions, String password) { 173 174 BoxSharedLink sharedLink = new BoxSharedLink(access, unshareDate, permissions, password); 175 Info info = new Info(); 176 info.setSharedLink(sharedLink); 177 178 this.updateInfo(info); 179 return info.getSharedLink(); 180 } 181 182 /** 183 * Adds new {@link BoxWebHook} to this {@link BoxFile}. 184 * 185 * @param address {@link BoxWebHook.Info#getAddress()} 186 * @param triggers {@link BoxWebHook.Info#getTriggers()} 187 * @return created {@link BoxWebHook.Info} 188 */ 189 public BoxWebHook.Info addWebHook(URL address, BoxWebHook.Trigger... triggers) { 190 return BoxWebHook.create(this, address, triggers); 191 } 192 193 /** 194 * Adds a comment to this file. The message can contain @mentions by using the string @[userid:username] anywhere 195 * within the message, where userid and username are the ID and username of the person being mentioned. 196 * 197 * @param message the comment's message. 198 * @return information about the newly added comment. 199 * @see <a href="https://developers.box.com/docs/#comments-add-a-comment-to-an-item">the tagged_message field 200 * for including @mentions.</a> 201 */ 202 public BoxComment.Info addComment(String message) { 203 JsonObject itemJSON = new JsonObject(); 204 itemJSON.add("type", "file"); 205 itemJSON.add("id", this.getID()); 206 207 JsonObject requestJSON = new JsonObject(); 208 requestJSON.add("item", itemJSON); 209 if (BoxComment.messageContainsMention(message)) { 210 requestJSON.add("tagged_message", message); 211 } else { 212 requestJSON.add("message", message); 213 } 214 215 URL url = ADD_COMMENT_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 216 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 217 request.setBody(requestJSON.toString()); 218 BoxJSONResponse response = (BoxJSONResponse) request.send(); 219 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 220 221 BoxComment addedComment = new BoxComment(this.getAPI(), responseJSON.get("id").asString()); 222 return addedComment.new Info(responseJSON); 223 } 224 225 /** 226 * Adds a new task to this file. The task can have an optional message to include, and a due date. 227 * 228 * @param action the action the task assignee will be prompted to do. 229 * @param message an optional message to include with the task. 230 * @param dueAt the day at which this task is due. 231 * @return information about the newly added task. 232 */ 233 public BoxTask.Info addTask(BoxTask.Action action, String message, Date dueAt) { 234 JsonObject itemJSON = new JsonObject(); 235 itemJSON.add("type", "file"); 236 itemJSON.add("id", this.getID()); 237 238 JsonObject requestJSON = new JsonObject(); 239 requestJSON.add("item", itemJSON); 240 requestJSON.add("action", action.toJSONString()); 241 242 if (message != null && !message.isEmpty()) { 243 requestJSON.add("message", message); 244 } 245 246 if (dueAt != null) { 247 requestJSON.add("due_at", BoxDateFormat.format(dueAt)); 248 } 249 250 URL url = ADD_TASK_URL_TEMPLATE.build(this.getAPI().getBaseURL()); 251 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 252 request.setBody(requestJSON.toString()); 253 BoxJSONResponse response = (BoxJSONResponse) request.send(); 254 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 255 256 BoxTask addedTask = new BoxTask(this.getAPI(), responseJSON.get("id").asString()); 257 return addedTask.new Info(responseJSON); 258 } 259 260 /** 261 * Gets an expiring URL for downloading a file directly from Box. This can be user, 262 * for example, for sending as a redirect to a browser to cause the browser 263 * to download the file directly from Box. 264 * 265 * @return the temporary download URL 266 */ 267 public URL getDownloadURL() { 268 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 269 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 270 request.setFollowRedirects(false); 271 272 BoxRedirectResponse response = (BoxRedirectResponse) request.send(); 273 274 return response.getRedirectURL(); 275 } 276 277 /** 278 * Downloads the contents of this file to a given OutputStream. 279 * 280 * @param output the stream to where the file will be written. 281 */ 282 public void download(OutputStream output) { 283 this.download(output, null); 284 } 285 286 /** 287 * Downloads the contents of this file to a given OutputStream while reporting the progress to a ProgressListener. 288 * 289 * @param output the stream to where the file will be written. 290 * @param listener a listener for monitoring the download's progress. 291 */ 292 public void download(OutputStream output, ProgressListener listener) { 293 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 294 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 295 BoxAPIResponse response = request.send(); 296 InputStream input = response.getBody(listener); 297 298 byte[] buffer = new byte[BUFFER_SIZE]; 299 try { 300 int n = input.read(buffer); 301 while (n != -1) { 302 output.write(buffer, 0, n); 303 n = input.read(buffer); 304 } 305 } catch (IOException e) { 306 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 307 } finally { 308 response.disconnect(); 309 } 310 } 311 312 /** 313 * Downloads a part of this file's contents, starting at specified byte offset. 314 * 315 * @param output the stream to where the file will be written. 316 * @param offset the byte offset at which to start the download. 317 */ 318 public void downloadRange(OutputStream output, long offset) { 319 this.downloadRange(output, offset, -1); 320 } 321 322 /** 323 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd. 324 * 325 * @param output the stream to where the file will be written. 326 * @param rangeStart the byte offset at which to start the download. 327 * @param rangeEnd the byte offset at which to stop the download. 328 */ 329 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd) { 330 this.downloadRange(output, rangeStart, rangeEnd, null); 331 } 332 333 /** 334 * Downloads a part of this file's contents, starting at rangeStart and stopping at rangeEnd, while reporting the 335 * progress to a ProgressListener. 336 * 337 * @param output the stream to where the file will be written. 338 * @param rangeStart the byte offset at which to start the download. 339 * @param rangeEnd the byte offset at which to stop the download. 340 * @param listener a listener for monitoring the download's progress. 341 */ 342 public void downloadRange(OutputStream output, long rangeStart, long rangeEnd, ProgressListener listener) { 343 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 344 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 345 if (rangeEnd > 0) { 346 request.addHeader("Range", String.format("bytes=%s-%s", Long.toString(rangeStart), 347 Long.toString(rangeEnd))); 348 } else { 349 request.addHeader("Range", String.format("bytes=%s-", Long.toString(rangeStart))); 350 } 351 352 BoxAPIResponse response = request.send(); 353 InputStream input = response.getBody(listener); 354 355 byte[] buffer = new byte[BUFFER_SIZE]; 356 try { 357 int n = input.read(buffer); 358 while (n != -1) { 359 output.write(buffer, 0, n); 360 n = input.read(buffer); 361 } 362 } catch (IOException e) { 363 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 364 } finally { 365 response.disconnect(); 366 } 367 } 368 369 @Override 370 public BoxFile.Info copy(BoxFolder destination) { 371 return this.copy(destination, null); 372 } 373 374 @Override 375 public BoxFile.Info copy(BoxFolder destination, String newName) { 376 URL url = COPY_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 377 378 JsonObject parent = new JsonObject(); 379 parent.add("id", destination.getID()); 380 381 JsonObject copyInfo = new JsonObject(); 382 copyInfo.add("parent", parent); 383 if (newName != null) { 384 copyInfo.add("name", newName); 385 } 386 387 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 388 request.setBody(copyInfo.toString()); 389 BoxJSONResponse response = (BoxJSONResponse) request.send(); 390 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 391 BoxFile copiedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 392 return copiedFile.new Info(responseJSON); 393 } 394 395 /** 396 * Deletes this file by moving it to the trash. 397 */ 398 public void delete() { 399 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 400 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 401 BoxAPIResponse response = request.send(); 402 response.disconnect(); 403 } 404 405 @Override 406 public BoxItem.Info move(BoxFolder destination) { 407 return this.move(destination, null); 408 } 409 410 @Override 411 public BoxItem.Info move(BoxFolder destination, String newName) { 412 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 413 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 414 415 JsonObject parent = new JsonObject(); 416 parent.add("id", destination.getID()); 417 418 JsonObject updateInfo = new JsonObject(); 419 updateInfo.add("parent", parent); 420 if (newName != null) { 421 updateInfo.add("name", newName); 422 } 423 424 request.setBody(updateInfo.toString()); 425 BoxJSONResponse response = (BoxJSONResponse) request.send(); 426 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 427 BoxFile movedFile = new BoxFile(this.getAPI(), responseJSON.get("id").asString()); 428 return movedFile.new Info(responseJSON); 429 } 430 431 /** 432 * Renames this file. 433 * 434 * @param newName the new name of the file. 435 */ 436 public void rename(String newName) { 437 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 438 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 439 440 JsonObject updateInfo = new JsonObject(); 441 updateInfo.add("name", newName); 442 443 request.setBody(updateInfo.toString()); 444 BoxAPIResponse response = request.send(); 445 response.disconnect(); 446 } 447 448 @Override 449 public BoxFile.Info getInfo() { 450 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 451 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 452 BoxJSONResponse response = (BoxJSONResponse) request.send(); 453 return new Info(response.getJSON()); 454 } 455 456 @Override 457 public BoxFile.Info getInfo(String... fields) { 458 String queryString = new QueryStringBuilder().appendParam("fields", fields).toString(); 459 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 460 461 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 462 BoxJSONResponse response = (BoxJSONResponse) request.send(); 463 return new Info(response.getJSON()); 464 } 465 466 /** 467 * Gets information about this item including a specified set of representations. 468 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 469 * 470 * @param representationHints hints for representations to be retrieved 471 * @param fields the fields to retrieve. 472 * @return info about this item containing only the specified fields, including representations. 473 */ 474 public BoxFile.Info getInfoWithRepresentations(String representationHints, String... fields) { 475 if (representationHints.matches(Representation.X_REP_HINTS_PATTERN)) { 476 //Since the user intends to get representations, add it to fields, even if user has missed it 477 Set<String> fieldsSet = new HashSet<String>(Arrays.asList(fields)); 478 fieldsSet.add("representations"); 479 String queryString = new QueryStringBuilder().appendParam("fields", 480 fieldsSet.toArray(new String[fieldsSet.size()])).toString(); 481 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 482 483 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 484 request.addHeader("X-Rep-Hints", representationHints); 485 BoxJSONResponse response = (BoxJSONResponse) request.send(); 486 return new Info(response.getJSON()); 487 } else { 488 throw new BoxAPIException("Represention hints is not valid." 489 + " Refer documention on how to construct X-Rep-Hints Header"); 490 } 491 } 492 493 /** 494 * Fetches the contents of a file representation and writes them to the provided output stream. 495 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 496 * @param representationHint the X-Rep-Hints query for the representation to fetch. 497 * @param output the output stream to write the contents to. 498 */ 499 public void getRepresentationContent(String representationHint, OutputStream output) { 500 501 this.getRepresentationContent(representationHint, "", output); 502 } 503 504 /** 505 * Fetches the contents of a file representation with asset path and writes them to the provided output stream. 506 * @see <a href=https://developer.box.com/reference#section-x-rep-hints-header>X-Rep-Hints Header</a> 507 * @param representationHint the X-Rep-Hints query for the representation to fetch. 508 * @param assetPath the path of the asset for representations containing multiple files. 509 * @param output the output stream to write the contents to. 510 */ 511 public void getRepresentationContent(String representationHint, String assetPath, OutputStream output) { 512 513 List<Representation> reps = this.getInfoWithRepresentations(representationHint).getRepresentations(); 514 if (reps.size() < 1) { 515 throw new BoxAPIException("No matching representations found"); 516 } 517 Representation representation = reps.get(0); 518 String repState = representation.getStatus().getState(); 519 520 if (repState.equals("viewable") || repState.equals("success")) { 521 522 this.makeRepresentationContentRequest(representation.getContent().getUrlTemplate(), 523 assetPath, output); 524 return; 525 } else if (repState.equals("pending") || repState.equals("none")) { 526 527 String repContentURLString = null; 528 while (repContentURLString == null) { 529 repContentURLString = this.pollRepInfo(representation.getInfo().getUrl()); 530 } 531 532 this.makeRepresentationContentRequest(repContentURLString, assetPath, output); 533 return; 534 535 } else if (repState.equals("error")) { 536 537 throw new BoxAPIException("Representation had error status"); 538 } else { 539 540 throw new BoxAPIException("Representation had unknown status"); 541 } 542 543 } 544 545 private String pollRepInfo(URL infoURL) { 546 547 BoxAPIRequest infoRequest = new BoxAPIRequest(this.getAPI(), infoURL, HttpMethod.GET); 548 BoxJSONResponse infoResponse = (BoxJSONResponse) infoRequest.send(); 549 JsonObject response = infoResponse.getJsonObject(); 550 551 Representation rep = new Representation(response); 552 553 String repState = rep.getStatus().getState(); 554 555 if (repState.equals("viewable") || repState.equals("success")) { 556 557 return rep.getContent().getUrlTemplate(); 558 } else if (repState.equals("pending") || repState.equals("none")) { 559 560 return null; 561 562 } else if (repState.equals("error")) { 563 564 throw new BoxAPIException("Representation had error status"); 565 } else { 566 567 throw new BoxAPIException("Representation had unknown status"); 568 } 569 } 570 571 private void makeRepresentationContentRequest(String representationURLTemplate, String assetPath, 572 OutputStream output) { 573 574 try { 575 576 URL repURL = new URL(representationURLTemplate.replace("{+asset_path}", assetPath)); 577 BoxAPIRequest repContentReq = new BoxAPIRequest(this.getAPI(), repURL, HttpMethod.GET); 578 579 BoxAPIResponse contentResponse = repContentReq.send(); 580 581 InputStream input = contentResponse.getBody(); 582 583 byte[] buffer = new byte[BUFFER_SIZE]; 584 try { 585 int n = input.read(buffer); 586 while (n != -1) { 587 output.write(buffer, 0, n); 588 n = input.read(buffer); 589 } 590 } catch (IOException e) { 591 throw new BoxAPIException("Couldn't connect to the Box API due to a network error.", e); 592 } finally { 593 contentResponse.disconnect(); 594 } 595 } catch (MalformedURLException ex) { 596 597 throw new BoxAPIException("Could not generate representation content URL"); 598 } 599 } 600 601 /** 602 * Updates the information about this file with any info fields that have been modified locally. 603 * 604 * <p>The only fields that will be updated are the ones that have been modified locally. For example, the following 605 * code won't update any information (or even send a network request) since none of the info's fields were 606 * changed:</p> 607 * 608 * <pre>BoxFile file = new File(api, id); 609 * BoxFile.Info info = file.getInfo(); 610 * file.updateInfo(info);</pre> 611 * 612 * @param info the updated info. 613 */ 614 public void updateInfo(BoxFile.Info info) { 615 URL url = FILE_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 616 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 617 request.setBody(info.getPendingChanges()); 618 BoxJSONResponse response = (BoxJSONResponse) request.send(); 619 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 620 info.update(jsonObject); 621 } 622 623 /** 624 * Gets any previous versions of this file. Note that only users with premium accounts will be able to retrieve 625 * previous versions of their files. 626 * 627 * @return a list of previous file versions. 628 */ 629 public Collection<BoxFileVersion> getVersions() { 630 URL url = VERSIONS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 631 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 632 BoxJSONResponse response = (BoxJSONResponse) request.send(); 633 634 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 635 JsonArray entries = jsonObject.get("entries").asArray(); 636 Collection<BoxFileVersion> versions = new ArrayList<BoxFileVersion>(); 637 for (JsonValue entry : entries) { 638 versions.add(new BoxFileVersion(this.getAPI(), entry.asObject(), this.getID())); 639 } 640 641 return versions; 642 } 643 644 /** 645 * Checks if the file can be successfully uploaded by using the preflight check. 646 * 647 * @param name the name to give the uploaded file or null to use existing name. 648 * @param fileSize the size of the file used for account capacity calculations. 649 * @param parentID the ID of the parent folder that the new version is being uploaded to. 650 * @deprecated This method will be removed in future versions of the SDK; use canUploadVersion(String, long) instead 651 */ 652 @Deprecated 653 public void canUploadVersion(String name, long fileSize, String parentID) { 654 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 655 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 656 657 JsonObject parent = new JsonObject(); 658 parent.add("id", parentID); 659 660 JsonObject preflightInfo = new JsonObject(); 661 preflightInfo.add("parent", parent); 662 if (name != null) { 663 preflightInfo.add("name", name); 664 } 665 666 preflightInfo.add("size", fileSize); 667 668 request.setBody(preflightInfo.toString()); 669 BoxAPIResponse response = request.send(); 670 response.disconnect(); 671 } 672 673 /** 674 * Checks if a new version of the file can be uploaded with the specified name. 675 * @param name the new name for the file. 676 * @return whether or not the file version can be uploaded. 677 */ 678 public boolean canUploadVersion(String name) { 679 return this.canUploadVersion(name, 0); 680 } 681 682 /** 683 * Checks if a new version of the file can be uploaded with the specified name and size. 684 * @param name the new name for the file. 685 * @param fileSize the size of the new version content in bytes. 686 * @return whether or not the file version can be uploaded. 687 */ 688 public boolean canUploadVersion(String name, long fileSize) { 689 690 URL url = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 691 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "OPTIONS"); 692 693 JsonObject preflightInfo = new JsonObject(); 694 if (name != null) { 695 preflightInfo.add("name", name); 696 } 697 698 preflightInfo.add("size", fileSize); 699 700 request.setBody(preflightInfo.toString()); 701 try { 702 BoxAPIResponse response = request.send(); 703 704 return response.getResponseCode() == 200; 705 } catch (BoxAPIException ex) { 706 707 if (ex.getResponseCode() >= 400 && ex.getResponseCode() < 500) { 708 // This looks like an error response, menaing the upload would fail 709 return false; 710 } else { 711 // This looks like a network error or server error, rethrow exception 712 throw ex; 713 } 714 } 715 } 716 717 /** 718 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 719 * will be able to view and recover previous versions of the file. 720 * 721 * @param fileContent a stream containing the new file contents. 722 * @deprecated use uploadNewVersion() instead. 723 */ 724 @Deprecated 725 public void uploadVersion(InputStream fileContent) { 726 this.uploadVersion(fileContent, null); 727 } 728 729 /** 730 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 731 * will be able to view and recover previous versions of the file. 732 * 733 * @param fileContent a stream containing the new file contents. 734 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 735 * @deprecated use uploadNewVersion() instead. 736 */ 737 @Deprecated 738 public void uploadVersion(InputStream fileContent, String fileContentSHA1) { 739 this.uploadVersion(fileContent, fileContentSHA1, null); 740 } 741 742 /** 743 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 744 * will be able to view and recover previous versions of the file. 745 * 746 * @param fileContent a stream containing the new file contents. 747 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 748 * @param modified the date that the new version was modified. 749 * @deprecated use uploadNewVersion() instead. 750 */ 751 @Deprecated 752 public void uploadVersion(InputStream fileContent, String fileContentSHA1, Date modified) { 753 this.uploadVersion(fileContent, fileContentSHA1, modified, 0, null); 754 } 755 756 /** 757 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 758 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 759 * of the file. 760 * 761 * @param fileContent a stream containing the new file contents. 762 * @param modified the date that the new version was modified. 763 * @param fileSize the size of the file used for determining the progress of the upload. 764 * @param listener a listener for monitoring the upload's progress. 765 * @deprecated use uploadNewVersion() instead. 766 */ 767 @Deprecated 768 public void uploadVersion(InputStream fileContent, Date modified, long fileSize, ProgressListener listener) { 769 this.uploadVersion(fileContent, null, modified, fileSize, listener); 770 } 771 772 /** 773 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 774 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 775 * of the file. 776 * 777 * @param fileContent a stream containing the new file contents. 778 * @param fileContentSHA1 the SHA1 hash of the file contents. will be sent along in the Content-MD5 header 779 * @param modified the date that the new version was modified. 780 * @param fileSize the size of the file used for determining the progress of the upload. 781 * @param listener a listener for monitoring the upload's progress. 782 * @deprecated use uploadNewVersion() instead. 783 */ 784 @Deprecated 785 public void uploadVersion(InputStream fileContent, String fileContentSHA1, Date modified, long fileSize, 786 ProgressListener listener) { 787 this.uploadNewVersion(fileContent, fileContentSHA1, modified, fileSize, listener); 788 return; 789 } 790 791 /** 792 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 793 * will be able to view and recover previous versions of the file. 794 * 795 * @param fileContent a stream containing the new file contents. 796 * @return the uploaded file version. 797 */ 798 public BoxFile.Info uploadNewVersion(InputStream fileContent) { 799 return this.uploadNewVersion(fileContent, null); 800 } 801 802 /** 803 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 804 * will be able to view and recover previous versions of the file. 805 * 806 * @param fileContent a stream containing the new file contents. 807 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 808 * @return the uploaded file version. 809 */ 810 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1) { 811 return this.uploadNewVersion(fileContent, fileContentSHA1, null); 812 } 813 814 /** 815 * Uploads a new version of this file, replacing the current version. Note that only users with premium accounts 816 * will be able to view and recover previous versions of the file. 817 * 818 * @param fileContent a stream containing the new file contents. 819 * @param fileContentSHA1 a string containing the SHA1 hash of the new file contents. 820 * @param modified the date that the new version was modified. 821 * @return the uploaded file version. 822 */ 823 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1, Date modified) { 824 return this.uploadNewVersion(fileContent, fileContentSHA1, modified, 0, null); 825 } 826 827 /** 828 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 829 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 830 * of the file. 831 * 832 * @param fileContent a stream containing the new file contents. 833 * @param modified the date that the new version was modified. 834 * @param fileSize the size of the file used for determining the progress of the upload. 835 * @param listener a listener for monitoring the upload's progress. 836 * @return the uploaded file version. 837 */ 838 public BoxFile.Info uploadNewVersion(InputStream fileContent, Date modified, long fileSize, 839 ProgressListener listener) { 840 return this.uploadNewVersion(fileContent, null, modified, fileSize, listener); 841 } 842 843 /** 844 * Uploads a new version of this file, replacing the current version, while reporting the progress to a 845 * ProgressListener. Note that only users with premium accounts will be able to view and recover previous versions 846 * of the file. 847 * 848 * @param fileContent a stream containing the new file contents. 849 * @param fileContentSHA1 the SHA1 hash of the file contents. will be sent along in the Content-MD5 header 850 * @param modified the date that the new version was modified. 851 * @param fileSize the size of the file used for determining the progress of the upload. 852 * @param listener a listener for monitoring the upload's progress. 853 * @return the uploaded file version. 854 */ 855 public BoxFile.Info uploadNewVersion(InputStream fileContent, String fileContentSHA1, Date modified, long fileSize, 856 ProgressListener listener) { 857 URL uploadURL = CONTENT_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 858 BoxMultipartRequest request = new BoxMultipartRequest(getAPI(), uploadURL); 859 860 if (fileSize > 0) { 861 request.setFile(fileContent, "", fileSize); 862 } else { 863 request.setFile(fileContent, ""); 864 } 865 866 if (fileContentSHA1 != null) { 867 request.setContentSHA1(fileContentSHA1); 868 } 869 870 if (modified != null) { 871 request.putField("content_modified_at", modified); 872 } 873 874 BoxJSONResponse response; 875 if (listener == null) { 876 response = (BoxJSONResponse) request.send(); 877 } else { 878 response = (BoxJSONResponse) request.send(listener); 879 } 880 881 String fileJSON = response.getJsonObject().get("entries").asArray().get(0).toString(); 882 883 return new BoxFile.Info(fileJSON); 884 } 885 886 /** 887 * Gets an expiring URL for creating an embedded preview session. The URL will expire after 60 seconds and the 888 * preview session will expire after 60 minutes. 889 * 890 * @return the expiring preview link 891 */ 892 public URL getPreviewLink() { 893 BoxFile.Info info = this.getInfo("expiring_embed_link"); 894 895 return info.getPreviewLink(); 896 } 897 898 899 /** 900 * Retrieves a thumbnail, or smaller image representation, of this file. Sizes of 32x32, 64x64, 128x128, 901 * and 256x256 can be returned in the .png format and sizes of 32x32, 94x94, 160x160, and 320x320 can be returned 902 * in the .jpg format. 903 * 904 * @param fileType either PNG of JPG 905 * @param minWidth minimum width 906 * @param minHeight minimum height 907 * @param maxWidth maximum width 908 * @param maxHeight maximum height 909 * @return the byte array of the thumbnail image 910 */ 911 public byte[] getThumbnail(ThumbnailFileType fileType, int minWidth, int minHeight, int maxWidth, int maxHeight) { 912 QueryStringBuilder builder = new QueryStringBuilder(); 913 builder.appendParam("min_width", minWidth); 914 builder.appendParam("min_height", minHeight); 915 builder.appendParam("max_width", maxWidth); 916 builder.appendParam("max_height", maxHeight); 917 918 URLTemplate template; 919 if (fileType == ThumbnailFileType.PNG) { 920 template = GET_THUMBNAIL_PNG_TEMPLATE; 921 } else if (fileType == ThumbnailFileType.JPG) { 922 template = GET_THUMBNAIL_JPG_TEMPLATE; 923 } else { 924 throw new BoxAPIException("Unsupported thumbnail file type"); 925 } 926 URL url = template.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID()); 927 928 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 929 BoxAPIResponse response = request.send(); 930 931 ByteArrayOutputStream thumbOut = new ByteArrayOutputStream(); 932 InputStream body = response.getBody(); 933 byte[] buffer = new byte[BUFFER_SIZE]; 934 try { 935 int n = body.read(buffer); 936 while (n != -1) { 937 thumbOut.write(buffer, 0, n); 938 n = body.read(buffer); 939 } 940 } catch (IOException e) { 941 throw new BoxAPIException("Error reading thumbnail bytes from response body", e); 942 } finally { 943 response.disconnect(); 944 } 945 946 return thumbOut.toByteArray(); 947 } 948 949 /** 950 * Gets a list of any comments on this file. 951 * 952 * @return a list of comments on this file. 953 */ 954 public List<BoxComment.Info> getComments() { 955 URL url = GET_COMMENTS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 956 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 957 BoxJSONResponse response = (BoxJSONResponse) request.send(); 958 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 959 960 int totalCount = responseJSON.get("total_count").asInt(); 961 List<BoxComment.Info> comments = new ArrayList<BoxComment.Info>(totalCount); 962 JsonArray entries = responseJSON.get("entries").asArray(); 963 for (JsonValue value : entries) { 964 JsonObject commentJSON = value.asObject(); 965 BoxComment comment = new BoxComment(this.getAPI(), commentJSON.get("id").asString()); 966 BoxComment.Info info = comment.new Info(commentJSON); 967 comments.add(info); 968 } 969 970 return comments; 971 } 972 973 /** 974 * Gets a list of any tasks on this file with requested fields. 975 * 976 * @param fields optional fields to retrieve for this task. 977 * @return a list of tasks on this file. 978 */ 979 public List<BoxTask.Info> getTasks(String... fields) { 980 QueryStringBuilder builder = new QueryStringBuilder(); 981 if (fields.length > 0) { 982 builder.appendParam("fields", fields).toString(); 983 } 984 URL url = GET_TASKS_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), builder.toString(), this.getID()); 985 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 986 BoxJSONResponse response = (BoxJSONResponse) request.send(); 987 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 988 989 int totalCount = responseJSON.get("total_count").asInt(); 990 List<BoxTask.Info> tasks = new ArrayList<BoxTask.Info>(totalCount); 991 JsonArray entries = responseJSON.get("entries").asArray(); 992 for (JsonValue value : entries) { 993 JsonObject taskJSON = value.asObject(); 994 BoxTask task = new BoxTask(this.getAPI(), taskJSON.get("id").asString()); 995 BoxTask.Info info = task.new Info(taskJSON); 996 tasks.add(info); 997 } 998 999 return tasks; 1000 } 1001 1002 /** 1003 * Creates metadata on this file in the global properties template. 1004 * 1005 * @param metadata The new metadata values. 1006 * @return the metadata returned from the server. 1007 */ 1008 public Metadata createMetadata(Metadata metadata) { 1009 return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata); 1010 } 1011 1012 /** 1013 * Creates metadata on this file in the specified template type. 1014 * 1015 * @param typeName the metadata template type name. 1016 * @param metadata the new metadata values. 1017 * @return the metadata returned from the server. 1018 */ 1019 public Metadata createMetadata(String typeName, Metadata metadata) { 1020 String scope = Metadata.scopeBasedOnType(typeName); 1021 return this.createMetadata(typeName, scope, metadata); 1022 } 1023 1024 /** 1025 * Creates metadata on this file in the specified template type. 1026 * 1027 * @param typeName the metadata template type name. 1028 * @param scope the metadata scope (global or enterprise). 1029 * @param metadata the new metadata values. 1030 * @return the metadata returned from the server. 1031 */ 1032 public Metadata createMetadata(String typeName, String scope, Metadata metadata) { 1033 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1034 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST"); 1035 request.addHeader("Content-Type", "application/json"); 1036 request.setBody(metadata.toString()); 1037 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1038 return new Metadata(JsonObject.readFrom(response.getJSON())); 1039 } 1040 1041 /** 1042 * Locks a file. 1043 * 1044 * @return the lock returned from the server. 1045 */ 1046 public BoxLock lock() { 1047 return this.lock(null, false); 1048 } 1049 1050 /** 1051 * Locks a file. 1052 * 1053 * @param isDownloadPrevented is downloading of file prevented when locked. 1054 * @return the lock returned from the server. 1055 */ 1056 public BoxLock lock(boolean isDownloadPrevented) { 1057 return this.lock(null, isDownloadPrevented); 1058 } 1059 1060 /** 1061 * Locks a file. 1062 * 1063 * @param expiresAt expiration date of the lock. 1064 * @return the lock returned from the server. 1065 */ 1066 public BoxLock lock(Date expiresAt) { 1067 return this.lock(expiresAt, false); 1068 } 1069 1070 /** 1071 * Locks a file. 1072 * 1073 * @param expiresAt expiration date of the lock. 1074 * @param isDownloadPrevented is downloading of file prevented when locked. 1075 * @return the lock returned from the server. 1076 */ 1077 public BoxLock lock(Date expiresAt, boolean isDownloadPrevented) { 1078 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 1079 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1080 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1081 1082 JsonObject lockConfig = new JsonObject(); 1083 lockConfig.add("type", "lock"); 1084 if (expiresAt != null) { 1085 lockConfig.add("expires_at", BoxDateFormat.format(expiresAt)); 1086 } 1087 lockConfig.add("is_download_prevented", isDownloadPrevented); 1088 1089 JsonObject requestJSON = new JsonObject(); 1090 requestJSON.add("lock", lockConfig); 1091 request.setBody(requestJSON.toString()); 1092 1093 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1094 1095 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 1096 JsonValue lockValue = responseJSON.get("lock"); 1097 JsonObject lockJSON = JsonObject.readFrom(lockValue.toString()); 1098 1099 return new BoxLock(lockJSON, this.getAPI()); 1100 } 1101 1102 /** 1103 * Unlocks a file. 1104 */ 1105 public void unlock() { 1106 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 1107 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1108 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1109 1110 JsonObject lockObject = new JsonObject(); 1111 lockObject.add("lock", JsonObject.NULL); 1112 1113 request.setBody(lockObject.toString()); 1114 request.send(); 1115 } 1116 1117 /** 1118 * Used to retrieve all metadata associated with the file. 1119 * 1120 * @param fields the optional fields to retrieve. 1121 * @return An iterable of metadata instances associated with the file. 1122 */ 1123 public Iterable<Metadata> getAllMetadata(String... fields) { 1124 return Metadata.getAllMetadata(this, fields); 1125 } 1126 1127 /** 1128 * Gets the file properties metadata. 1129 * 1130 * @return the metadata returned from the server. 1131 */ 1132 public Metadata getMetadata() { 1133 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 1134 } 1135 1136 /** 1137 * Gets the file metadata of specified template type. 1138 * 1139 * @param typeName the metadata template type name. 1140 * @return the metadata returned from the server. 1141 */ 1142 public Metadata getMetadata(String typeName) { 1143 String scope = Metadata.scopeBasedOnType(typeName); 1144 return this.getMetadata(typeName, scope); 1145 } 1146 1147 /** 1148 * Gets the file metadata of specified template type. 1149 * 1150 * @param typeName the metadata template type name. 1151 * @param scope the metadata scope (global or enterprise). 1152 * @return the metadata returned from the server. 1153 */ 1154 public Metadata getMetadata(String typeName, String scope) { 1155 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1156 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 1157 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1158 return new Metadata(JsonObject.readFrom(response.getJSON())); 1159 } 1160 1161 /** 1162 * Updates the file metadata. 1163 * 1164 * @param metadata the new metadata values. 1165 * @return the metadata returned from the server. 1166 */ 1167 public Metadata updateMetadata(Metadata metadata) { 1168 String scope; 1169 if (metadata.getScope().equals(Metadata.GLOBAL_METADATA_SCOPE)) { 1170 scope = Metadata.GLOBAL_METADATA_SCOPE; 1171 } else { 1172 scope = Metadata.ENTERPRISE_METADATA_SCOPE; 1173 } 1174 1175 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), 1176 scope, metadata.getTemplateName()); 1177 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1178 request.addHeader("Content-Type", "application/json-patch+json"); 1179 request.setBody(metadata.getPatch()); 1180 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1181 return new Metadata(JsonObject.readFrom(response.getJSON())); 1182 } 1183 1184 /** 1185 * Deletes the file properties metadata. 1186 */ 1187 public void deleteMetadata() { 1188 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 1189 } 1190 1191 /** 1192 * Deletes the file metadata of specified template type. 1193 * 1194 * @param typeName the metadata template type name. 1195 */ 1196 public void deleteMetadata(String typeName) { 1197 String scope = Metadata.scopeBasedOnType(typeName); 1198 this.deleteMetadata(typeName, scope); 1199 } 1200 1201 /** 1202 * Deletes the file metadata of specified template type. 1203 * 1204 * @param typeName the metadata template type name. 1205 * @param scope the metadata scope (global or enterprise). 1206 */ 1207 public void deleteMetadata(String typeName, String scope) { 1208 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1209 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 1210 request.send(); 1211 } 1212 1213 /** 1214 * Used to retrieve the watermark for the file. 1215 * If the file does not have a watermark applied to it, a 404 Not Found will be returned by API. 1216 * 1217 * @param fields the fields to retrieve. 1218 * @return the watermark associated with the file. 1219 */ 1220 public BoxWatermark getWatermark(String... fields) { 1221 return this.getWatermark(FILE_URL_TEMPLATE, fields); 1222 } 1223 1224 /** 1225 * Used to apply or update the watermark for the file. 1226 * 1227 * @return the watermark associated with the file. 1228 */ 1229 public BoxWatermark applyWatermark() { 1230 return this.applyWatermark(FILE_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 1231 } 1232 1233 /** 1234 * Removes a watermark from the file. 1235 * If the file did not have a watermark applied to it, a 404 Not Found will be returned by API. 1236 */ 1237 public void removeWatermark() { 1238 this.removeWatermark(FILE_URL_TEMPLATE); 1239 } 1240 1241 /** 1242 * {@inheritDoc} 1243 */ 1244 @Override 1245 public BoxFile.Info setCollections(BoxCollection... collections) { 1246 JsonArray jsonArray = new JsonArray(); 1247 for (BoxCollection collection : collections) { 1248 JsonObject collectionJSON = new JsonObject(); 1249 collectionJSON.add("id", collection.getID()); 1250 jsonArray.add(collectionJSON); 1251 } 1252 JsonObject infoJSON = new JsonObject(); 1253 infoJSON.add("collections", jsonArray); 1254 1255 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 1256 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1257 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 1258 request.setBody(infoJSON.toString()); 1259 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1260 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 1261 return new Info(jsonObject); 1262 } 1263 1264 /** 1265 * Creates an upload session to create a new version of a file in chunks. 1266 * This will first verify that the version can be created and then open a session for uploading pieces of the file. 1267 * @param fileSize the size of the file that will be uploaded. 1268 * @return the created upload session instance. 1269 */ 1270 public BoxFileUploadSession.Info createUploadSession(long fileSize) { 1271 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1272 1273 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 1274 request.addHeader("Content-Type", "application/json"); 1275 1276 JsonObject body = new JsonObject(); 1277 body.add("file_size", fileSize); 1278 request.setBody(body.toString()); 1279 1280 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1281 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 1282 1283 String sessionId = jsonObject.get("id").asString(); 1284 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 1285 return session.new Info(jsonObject); 1286 } 1287 1288 /** 1289 * Creates a new version of a file. 1290 * @param inputStream the stream instance that contains the data. 1291 * @param fileSize the size of the file that will be uploaded. 1292 * @return the created file instance. 1293 * @throws InterruptedException when a thread execution is interrupted. 1294 * @throws IOException when reading a stream throws exception. 1295 */ 1296 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize) 1297 throws InterruptedException, IOException { 1298 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1299 return new LargeFileUpload().upload(this.getAPI(), inputStream, url, fileSize); 1300 } 1301 1302 /** 1303 * Creates a new version of a file using specified number of parallel http connections. 1304 * @param inputStream the stream instance that contains the data. 1305 * @param fileSize the size of the file that will be uploaded. 1306 * @param nParallelConnections number of parallel http connections to use 1307 * @param timeOut time to wait before killing the job 1308 * @param unit time unit for the time wait value 1309 * @return the created file instance. 1310 * @throws InterruptedException when a thread execution is interrupted. 1311 * @throws IOException when reading a stream throws exception. 1312 */ 1313 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, 1314 int nParallelConnections, long timeOut, TimeUnit unit) 1315 throws InterruptedException, IOException { 1316 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1317 return new LargeFileUpload(nParallelConnections, timeOut, unit) 1318 .upload(this.getAPI(), inputStream, url, fileSize); 1319 } 1320 1321 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 1322 Boolean notify, Boolean canViewPath) { 1323 1324 JsonObject itemField = new JsonObject(); 1325 itemField.add("id", this.getID()); 1326 itemField.add("type", "file"); 1327 1328 return BoxCollaboration.create(this.getAPI(), accessibleByField, itemField, role, notify, canViewPath); 1329 } 1330 1331 /** 1332 * Adds a collaborator to this file. 1333 * 1334 * @param collaborator the collaborator to add. 1335 * @param role the role of the collaborator. 1336 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1337 * @param canViewPath whether view path collaboration feature is enabled or not. 1338 * @return info about the new collaboration. 1339 */ 1340 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 1341 Boolean notify, Boolean canViewPath) { 1342 JsonObject accessibleByField = new JsonObject(); 1343 accessibleByField.add("id", collaborator.getID()); 1344 1345 if (collaborator instanceof BoxUser) { 1346 accessibleByField.add("type", "user"); 1347 } else if (collaborator instanceof BoxGroup) { 1348 accessibleByField.add("type", "group"); 1349 } else { 1350 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 1351 } 1352 return this.collaborate(accessibleByField, role, notify, canViewPath); 1353 } 1354 1355 1356 /** 1357 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 1358 * account. 1359 * 1360 * @param email the email address of the collaborator to add. 1361 * @param role the role of the collaborator. 1362 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1363 * @param canViewPath whether view path collaboration feature is enabled or not. 1364 * @return info about the new collaboration. 1365 */ 1366 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 1367 Boolean notify, Boolean canViewPath) { 1368 JsonObject accessibleByField = new JsonObject(); 1369 accessibleByField.add("login", email); 1370 accessibleByField.add("type", "user"); 1371 1372 return this.collaborate(accessibleByField, role, notify, canViewPath); 1373 } 1374 1375 /** 1376 * Used to retrieve all collaborations associated with the item. 1377 * 1378 * @param fields the optional fields to retrieve. 1379 * @return An iterable of metadata instances associated with the item. 1380 */ 1381 public BoxResourceIterable<BoxCollaboration.Info> getAllFileCollaborations(String... fields) { 1382 return BoxCollaboration.getAllFileCollaborations(this.getAPI(), this.getID(), 1383 GET_COLLABORATORS_PAGE_SIZE, fields); 1384 1385 } 1386 1387 /** 1388 * Contains information about a BoxFile. 1389 */ 1390 public class Info extends BoxItem.Info { 1391 private String sha1; 1392 private String versionNumber; 1393 private long commentCount; 1394 private EnumSet<Permission> permissions; 1395 private String extension; 1396 private boolean isPackage; 1397 private BoxFileVersion version; 1398 private URL previewLink; 1399 private BoxLock lock; 1400 private boolean isWatermarked; 1401 private JsonObject metadata; 1402 private Map<String, Map<String, Metadata>> metadataMap; 1403 private List<Representation> representations; 1404 1405 /** 1406 * Constructs an empty Info object. 1407 */ 1408 public Info() { 1409 super(); 1410 } 1411 1412 /** 1413 * Constructs an Info object by parsing information from a JSON string. 1414 * 1415 * @param json the JSON string to parse. 1416 */ 1417 public Info(String json) { 1418 super(json); 1419 } 1420 1421 /** 1422 * Constructs an Info object using an already parsed JSON object. 1423 * 1424 * @param jsonObject the parsed JSON object. 1425 */ 1426 public Info(JsonObject jsonObject) { 1427 super(jsonObject); 1428 } 1429 1430 @Override 1431 public BoxFile getResource() { 1432 return BoxFile.this; 1433 } 1434 1435 /** 1436 * Gets the SHA1 hash of the file. 1437 * 1438 * @return the SHA1 hash of the file. 1439 */ 1440 public String getSha1() { 1441 return this.sha1; 1442 } 1443 1444 /** 1445 * Gets the lock of the file. 1446 * 1447 * @return the lock of the file. 1448 */ 1449 public BoxLock getLock() { 1450 return this.lock; 1451 } 1452 1453 /** 1454 * Gets the current version number of the file. 1455 * 1456 * @return the current version number of the file. 1457 */ 1458 public String getVersionNumber() { 1459 return this.versionNumber; 1460 } 1461 1462 /** 1463 * Gets the number of comments on the file. 1464 * 1465 * @return the number of comments on the file. 1466 */ 1467 public long getCommentCount() { 1468 return this.commentCount; 1469 } 1470 1471 /** 1472 * Gets the permissions that the current user has on the file. 1473 * 1474 * @return the permissions that the current user has on the file. 1475 */ 1476 public EnumSet<Permission> getPermissions() { 1477 return this.permissions; 1478 } 1479 1480 /** 1481 * Gets the extension suffix of the file, excluding the dot. 1482 * 1483 * @return the extension of the file. 1484 */ 1485 public String getExtension() { 1486 return this.extension; 1487 } 1488 1489 /** 1490 * Gets whether or not the file is an OSX package. 1491 * 1492 * @return true if the file is an OSX package; otherwise false. 1493 */ 1494 public boolean getIsPackage() { 1495 return this.isPackage; 1496 } 1497 1498 /** 1499 * Gets the current version details of the file. 1500 * 1501 * @return the current version details of the file. 1502 */ 1503 public BoxFileVersion getVersion() { 1504 return this.version; 1505 } 1506 1507 /** 1508 * Gets the current expiring preview link. 1509 * 1510 * @return the expiring preview link 1511 */ 1512 public URL getPreviewLink() { 1513 return this.previewLink; 1514 } 1515 1516 /** 1517 * Gets flag indicating whether this file is Watermarked. 1518 * 1519 * @return whether the file is watermarked or not 1520 */ 1521 public boolean getIsWatermarked() { 1522 return this.isWatermarked; 1523 } 1524 1525 /** 1526 * Gets the metadata on this file associated with a specified scope and template. 1527 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. If no result is found 1528 * then makes an API call to get metadata 1529 * @param templateName the metadata template type name. 1530 * @param scope the scope of the template (usually "global" or "enterprise"). 1531 * @return the metadata returned from the server. 1532 */ 1533 public Metadata getMetadata(String templateName, String scope) { 1534 try { 1535 return this.metadataMap.get(scope).get(templateName); 1536 } catch (NullPointerException e) { 1537 return null; 1538 } 1539 } 1540 1541 /** 1542 * Get file's representations. 1543 * @return list of representations 1544 */ 1545 public List<Representation> getRepresentations() { 1546 return this.representations; 1547 } 1548 1549 @Override 1550 protected void parseJSONMember(JsonObject.Member member) { 1551 super.parseJSONMember(member); 1552 1553 String memberName = member.getName(); 1554 JsonValue value = member.getValue(); 1555 if (memberName.equals("sha1")) { 1556 this.sha1 = value.asString(); 1557 } else if (memberName.equals("version_number")) { 1558 this.versionNumber = value.asString(); 1559 } else if (memberName.equals("comment_count")) { 1560 this.commentCount = value.asLong(); 1561 } else if (memberName.equals("permissions")) { 1562 this.permissions = this.parsePermissions(value.asObject()); 1563 } else if (memberName.equals("extension")) { 1564 this.extension = value.asString(); 1565 } else if (memberName.equals("is_package")) { 1566 this.isPackage = value.asBoolean(); 1567 } else if (memberName.equals("file_version")) { 1568 this.version = this.parseFileVersion(value.asObject()); 1569 } else if (memberName.equals("expiring_embed_link")) { 1570 try { 1571 String urlString = member.getValue().asObject().get("url").asString(); 1572 this.previewLink = new URL(urlString); 1573 } catch (MalformedURLException e) { 1574 throw new BoxAPIException("Couldn't parse expiring_embed_link/url for file", e); 1575 } 1576 } else if (memberName.equals("lock")) { 1577 if (value.isNull()) { 1578 this.lock = null; 1579 } else { 1580 this.lock = new BoxLock(value.asObject(), BoxFile.this.getAPI()); 1581 } 1582 } else if (memberName.equals("watermark_info")) { 1583 JsonObject jsonObject = value.asObject(); 1584 this.isWatermarked = jsonObject.get("is_watermarked").asBoolean(); 1585 } else if (memberName.equals("metadata")) { 1586 JsonObject jsonObject = value.asObject(); 1587 this.metadataMap = Parsers.parseAndPopulateMetadataMap(jsonObject); 1588 } else if (memberName.equals("representations")) { 1589 JsonObject jsonObject = value.asObject(); 1590 this.representations = Parsers.parseRepresentations(jsonObject); 1591 } 1592 } 1593 1594 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1595 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1596 for (JsonObject.Member member : jsonObject) { 1597 JsonValue value = member.getValue(); 1598 if (value.isNull() || !value.asBoolean()) { 1599 continue; 1600 } 1601 1602 String memberName = member.getName(); 1603 if (memberName.equals("can_download")) { 1604 permissions.add(Permission.CAN_DOWNLOAD); 1605 } else if (memberName.equals("can_upload")) { 1606 permissions.add(Permission.CAN_UPLOAD); 1607 } else if (memberName.equals("can_rename")) { 1608 permissions.add(Permission.CAN_RENAME); 1609 } else if (memberName.equals("can_delete")) { 1610 permissions.add(Permission.CAN_DELETE); 1611 } else if (memberName.equals("can_share")) { 1612 permissions.add(Permission.CAN_SHARE); 1613 } else if (memberName.equals("can_set_share_access")) { 1614 permissions.add(Permission.CAN_SET_SHARE_ACCESS); 1615 } else if (memberName.equals("can_preview")) { 1616 permissions.add(Permission.CAN_PREVIEW); 1617 } else if (memberName.equals("can_comment")) { 1618 permissions.add(Permission.CAN_COMMENT); 1619 } 1620 } 1621 1622 return permissions; 1623 } 1624 1625 private BoxFileVersion parseFileVersion(JsonObject jsonObject) { 1626 return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID()); 1627 } 1628 } 1629 1630 /** 1631 * Enumerates the possible permissions that a user can have on a file. 1632 */ 1633 public enum Permission { 1634 /** 1635 * The user can download the file. 1636 */ 1637 CAN_DOWNLOAD("can_download"), 1638 1639 /** 1640 * The user can upload new versions of the file. 1641 */ 1642 CAN_UPLOAD("can_upload"), 1643 1644 /** 1645 * The user can rename the file. 1646 */ 1647 CAN_RENAME("can_rename"), 1648 1649 /** 1650 * The user can delete the file. 1651 */ 1652 CAN_DELETE("can_delete"), 1653 1654 /** 1655 * The user can share the file. 1656 */ 1657 CAN_SHARE("can_share"), 1658 1659 /** 1660 * The user can set the access level for shared links to the file. 1661 */ 1662 CAN_SET_SHARE_ACCESS("can_set_share_access"), 1663 1664 /** 1665 * The user can preview the file. 1666 */ 1667 CAN_PREVIEW("can_preview"), 1668 1669 /** 1670 * The user can comment on the file. 1671 */ 1672 CAN_COMMENT("can_comment"); 1673 1674 private final String jsonValue; 1675 1676 private Permission(String jsonValue) { 1677 this.jsonValue = jsonValue; 1678 } 1679 1680 static Permission fromJSONValue(String jsonValue) { 1681 return Permission.valueOf(jsonValue.toUpperCase()); 1682 } 1683 1684 String toJSONValue() { 1685 return this.jsonValue; 1686 } 1687 } 1688 1689}