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. 975 * 976 * @return a list of tasks on this file. 977 */ 978 public List<BoxTask.Info> getTasks() { 979 URL url = GET_TASKS_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID()); 980 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 981 BoxJSONResponse response = (BoxJSONResponse) request.send(); 982 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 983 984 int totalCount = responseJSON.get("total_count").asInt(); 985 List<BoxTask.Info> tasks = new ArrayList<BoxTask.Info>(totalCount); 986 JsonArray entries = responseJSON.get("entries").asArray(); 987 for (JsonValue value : entries) { 988 JsonObject taskJSON = value.asObject(); 989 BoxTask task = new BoxTask(this.getAPI(), taskJSON.get("id").asString()); 990 BoxTask.Info info = task.new Info(taskJSON); 991 tasks.add(info); 992 } 993 994 return tasks; 995 } 996 997 /** 998 * Creates metadata on this file in the global properties template. 999 * 1000 * @param metadata The new metadata values. 1001 * @return the metadata returned from the server. 1002 */ 1003 public Metadata createMetadata(Metadata metadata) { 1004 return this.createMetadata(Metadata.DEFAULT_METADATA_TYPE, metadata); 1005 } 1006 1007 /** 1008 * Creates metadata on this file in the specified template type. 1009 * 1010 * @param typeName the metadata template type name. 1011 * @param metadata the new metadata values. 1012 * @return the metadata returned from the server. 1013 */ 1014 public Metadata createMetadata(String typeName, Metadata metadata) { 1015 String scope = Metadata.scopeBasedOnType(typeName); 1016 return this.createMetadata(typeName, scope, metadata); 1017 } 1018 1019 /** 1020 * Creates metadata on this file in the specified template type. 1021 * 1022 * @param typeName the metadata template type name. 1023 * @param scope the metadata scope (global or enterprise). 1024 * @param metadata the new metadata values. 1025 * @return the metadata returned from the server. 1026 */ 1027 public Metadata createMetadata(String typeName, String scope, Metadata metadata) { 1028 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1029 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "POST"); 1030 request.addHeader("Content-Type", "application/json"); 1031 request.setBody(metadata.toString()); 1032 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1033 return new Metadata(JsonObject.readFrom(response.getJSON())); 1034 } 1035 1036 /** 1037 * Locks a file. 1038 * 1039 * @return the lock returned from the server. 1040 */ 1041 public BoxLock lock() { 1042 return this.lock(null, false); 1043 } 1044 1045 /** 1046 * Locks a file. 1047 * 1048 * @param isDownloadPrevented is downloading of file prevented when locked. 1049 * @return the lock returned from the server. 1050 */ 1051 public BoxLock lock(boolean isDownloadPrevented) { 1052 return this.lock(null, isDownloadPrevented); 1053 } 1054 1055 /** 1056 * Locks a file. 1057 * 1058 * @param expiresAt expiration date of the lock. 1059 * @return the lock returned from the server. 1060 */ 1061 public BoxLock lock(Date expiresAt) { 1062 return this.lock(expiresAt, false); 1063 } 1064 1065 /** 1066 * Locks a file. 1067 * 1068 * @param expiresAt expiration date of the lock. 1069 * @param isDownloadPrevented is downloading of file prevented when locked. 1070 * @return the lock returned from the server. 1071 */ 1072 public BoxLock lock(Date expiresAt, boolean isDownloadPrevented) { 1073 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 1074 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1075 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1076 1077 JsonObject lockConfig = new JsonObject(); 1078 lockConfig.add("type", "lock"); 1079 if (expiresAt != null) { 1080 lockConfig.add("expires_at", BoxDateFormat.format(expiresAt)); 1081 } 1082 lockConfig.add("is_download_prevented", isDownloadPrevented); 1083 1084 JsonObject requestJSON = new JsonObject(); 1085 requestJSON.add("lock", lockConfig); 1086 request.setBody(requestJSON.toString()); 1087 1088 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1089 1090 JsonObject responseJSON = JsonObject.readFrom(response.getJSON()); 1091 JsonValue lockValue = responseJSON.get("lock"); 1092 JsonObject lockJSON = JsonObject.readFrom(lockValue.toString()); 1093 1094 return new BoxLock(lockJSON, this.getAPI()); 1095 } 1096 1097 /** 1098 * Unlocks a file. 1099 */ 1100 public void unlock() { 1101 String queryString = new QueryStringBuilder().appendParam("fields", "lock").toString(); 1102 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1103 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1104 1105 JsonObject lockObject = new JsonObject(); 1106 lockObject.add("lock", JsonObject.NULL); 1107 1108 request.setBody(lockObject.toString()); 1109 request.send(); 1110 } 1111 1112 /** 1113 * Used to retrieve all metadata associated with the file. 1114 * 1115 * @param fields the optional fields to retrieve. 1116 * @return An iterable of metadata instances associated with the file. 1117 */ 1118 public Iterable<Metadata> getAllMetadata(String... fields) { 1119 return Metadata.getAllMetadata(this, fields); 1120 } 1121 1122 /** 1123 * Gets the file properties metadata. 1124 * 1125 * @return the metadata returned from the server. 1126 */ 1127 public Metadata getMetadata() { 1128 return this.getMetadata(Metadata.DEFAULT_METADATA_TYPE); 1129 } 1130 1131 /** 1132 * Gets the file metadata of specified template type. 1133 * 1134 * @param typeName the metadata template type name. 1135 * @return the metadata returned from the server. 1136 */ 1137 public Metadata getMetadata(String typeName) { 1138 String scope = Metadata.scopeBasedOnType(typeName); 1139 return this.getMetadata(typeName, scope); 1140 } 1141 1142 /** 1143 * Gets the file metadata of specified template type. 1144 * 1145 * @param typeName the metadata template type name. 1146 * @param scope the metadata scope (global or enterprise). 1147 * @return the metadata returned from the server. 1148 */ 1149 public Metadata getMetadata(String typeName, String scope) { 1150 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1151 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "GET"); 1152 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1153 return new Metadata(JsonObject.readFrom(response.getJSON())); 1154 } 1155 1156 /** 1157 * Updates the file metadata. 1158 * 1159 * @param metadata the new metadata values. 1160 * @return the metadata returned from the server. 1161 */ 1162 public Metadata updateMetadata(Metadata metadata) { 1163 String scope; 1164 if (metadata.getScope().equals(Metadata.GLOBAL_METADATA_SCOPE)) { 1165 scope = Metadata.GLOBAL_METADATA_SCOPE; 1166 } else { 1167 scope = Metadata.ENTERPRISE_METADATA_SCOPE; 1168 } 1169 1170 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), 1171 scope, metadata.getTemplateName()); 1172 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "PUT"); 1173 request.addHeader("Content-Type", "application/json-patch+json"); 1174 request.setBody(metadata.getPatch()); 1175 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1176 return new Metadata(JsonObject.readFrom(response.getJSON())); 1177 } 1178 1179 /** 1180 * Deletes the file properties metadata. 1181 */ 1182 public void deleteMetadata() { 1183 this.deleteMetadata(Metadata.DEFAULT_METADATA_TYPE); 1184 } 1185 1186 /** 1187 * Deletes the file metadata of specified template type. 1188 * 1189 * @param typeName the metadata template type name. 1190 */ 1191 public void deleteMetadata(String typeName) { 1192 String scope = Metadata.scopeBasedOnType(typeName); 1193 this.deleteMetadata(typeName, scope); 1194 } 1195 1196 /** 1197 * Deletes the file metadata of specified template type. 1198 * 1199 * @param typeName the metadata template type name. 1200 * @param scope the metadata scope (global or enterprise). 1201 */ 1202 public void deleteMetadata(String typeName, String scope) { 1203 URL url = METADATA_URL_TEMPLATE.build(this.getAPI().getBaseURL(), this.getID(), scope, typeName); 1204 BoxAPIRequest request = new BoxAPIRequest(this.getAPI(), url, "DELETE"); 1205 request.send(); 1206 } 1207 1208 /** 1209 * Used to retrieve the watermark for the file. 1210 * If the file does not have a watermark applied to it, a 404 Not Found will be returned by API. 1211 * 1212 * @param fields the fields to retrieve. 1213 * @return the watermark associated with the file. 1214 */ 1215 public BoxWatermark getWatermark(String... fields) { 1216 return this.getWatermark(FILE_URL_TEMPLATE, fields); 1217 } 1218 1219 /** 1220 * Used to apply or update the watermark for the file. 1221 * 1222 * @return the watermark associated with the file. 1223 */ 1224 public BoxWatermark applyWatermark() { 1225 return this.applyWatermark(FILE_URL_TEMPLATE, BoxWatermark.WATERMARK_DEFAULT_IMPRINT); 1226 } 1227 1228 /** 1229 * Removes a watermark from the file. 1230 * If the file did not have a watermark applied to it, a 404 Not Found will be returned by API. 1231 */ 1232 public void removeWatermark() { 1233 this.removeWatermark(FILE_URL_TEMPLATE); 1234 } 1235 1236 /** 1237 * {@inheritDoc} 1238 */ 1239 @Override 1240 public BoxFile.Info setCollections(BoxCollection... collections) { 1241 JsonArray jsonArray = new JsonArray(); 1242 for (BoxCollection collection : collections) { 1243 JsonObject collectionJSON = new JsonObject(); 1244 collectionJSON.add("id", collection.getID()); 1245 jsonArray.add(collectionJSON); 1246 } 1247 JsonObject infoJSON = new JsonObject(); 1248 infoJSON.add("collections", jsonArray); 1249 1250 String queryString = new QueryStringBuilder().appendParam("fields", ALL_FIELDS).toString(); 1251 URL url = FILE_URL_TEMPLATE.buildWithQuery(this.getAPI().getBaseURL(), queryString, this.getID()); 1252 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "PUT"); 1253 request.setBody(infoJSON.toString()); 1254 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1255 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 1256 return new Info(jsonObject); 1257 } 1258 1259 /** 1260 * Creates an upload session to create a new version of a file in chunks. 1261 * This will first verify that the version can be created and then open a session for uploading pieces of the file. 1262 * @param fileSize the size of the file that will be uploaded. 1263 * @return the created upload session instance. 1264 */ 1265 public BoxFileUploadSession.Info createUploadSession(long fileSize) { 1266 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1267 1268 BoxJSONRequest request = new BoxJSONRequest(this.getAPI(), url, "POST"); 1269 request.addHeader("Content-Type", "application/json"); 1270 1271 JsonObject body = new JsonObject(); 1272 body.add("file_size", fileSize); 1273 request.setBody(body.toString()); 1274 1275 BoxJSONResponse response = (BoxJSONResponse) request.send(); 1276 JsonObject jsonObject = JsonObject.readFrom(response.getJSON()); 1277 1278 String sessionId = jsonObject.get("id").asString(); 1279 BoxFileUploadSession session = new BoxFileUploadSession(this.getAPI(), sessionId); 1280 return session.new Info(jsonObject); 1281 } 1282 1283 /** 1284 * Creates a new version of a file. 1285 * @param inputStream the stream instance that contains the data. 1286 * @param fileSize the size of the file that will be uploaded. 1287 * @return the created file instance. 1288 * @throws InterruptedException when a thread execution is interrupted. 1289 * @throws IOException when reading a stream throws exception. 1290 */ 1291 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize) 1292 throws InterruptedException, IOException { 1293 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1294 return new LargeFileUpload().upload(this.getAPI(), inputStream, url, fileSize); 1295 } 1296 1297 /** 1298 * Creates a new version of a file using specified number of parallel http connections. 1299 * @param inputStream the stream instance that contains the data. 1300 * @param fileSize the size of the file that will be uploaded. 1301 * @param nParallelConnections number of parallel http connections to use 1302 * @param timeOut time to wait before killing the job 1303 * @param unit time unit for the time wait value 1304 * @return the created file instance. 1305 * @throws InterruptedException when a thread execution is interrupted. 1306 * @throws IOException when reading a stream throws exception. 1307 */ 1308 public BoxFile.Info uploadLargeFile(InputStream inputStream, long fileSize, 1309 int nParallelConnections, long timeOut, TimeUnit unit) 1310 throws InterruptedException, IOException { 1311 URL url = UPLOAD_SESSION_URL_TEMPLATE.build(this.getAPI().getBaseUploadURL(), this.getID()); 1312 return new LargeFileUpload(nParallelConnections, timeOut, unit) 1313 .upload(this.getAPI(), inputStream, url, fileSize); 1314 } 1315 1316 private BoxCollaboration.Info collaborate(JsonObject accessibleByField, BoxCollaboration.Role role, 1317 Boolean notify, Boolean canViewPath) { 1318 1319 JsonObject itemField = new JsonObject(); 1320 itemField.add("id", this.getID()); 1321 itemField.add("type", "file"); 1322 1323 return BoxCollaboration.create(this.getAPI(), accessibleByField, itemField, role, notify, canViewPath); 1324 } 1325 1326 /** 1327 * Adds a collaborator to this file. 1328 * 1329 * @param collaborator the collaborator to add. 1330 * @param role the role of the collaborator. 1331 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1332 * @param canViewPath whether view path collaboration feature is enabled or not. 1333 * @return info about the new collaboration. 1334 */ 1335 public BoxCollaboration.Info collaborate(BoxCollaborator collaborator, BoxCollaboration.Role role, 1336 Boolean notify, Boolean canViewPath) { 1337 JsonObject accessibleByField = new JsonObject(); 1338 accessibleByField.add("id", collaborator.getID()); 1339 1340 if (collaborator instanceof BoxUser) { 1341 accessibleByField.add("type", "user"); 1342 } else if (collaborator instanceof BoxGroup) { 1343 accessibleByField.add("type", "group"); 1344 } else { 1345 throw new IllegalArgumentException("The given collaborator is of an unknown type."); 1346 } 1347 return this.collaborate(accessibleByField, role, notify, canViewPath); 1348 } 1349 1350 1351 /** 1352 * Adds a collaborator to this folder. An email will be sent to the collaborator if they don't already have a Box 1353 * account. 1354 * 1355 * @param email the email address of the collaborator to add. 1356 * @param role the role of the collaborator. 1357 * @param notify determines if the user (or all the users in the group) will receive email notifications. 1358 * @param canViewPath whether view path collaboration feature is enabled or not. 1359 * @return info about the new collaboration. 1360 */ 1361 public BoxCollaboration.Info collaborate(String email, BoxCollaboration.Role role, 1362 Boolean notify, Boolean canViewPath) { 1363 JsonObject accessibleByField = new JsonObject(); 1364 accessibleByField.add("login", email); 1365 accessibleByField.add("type", "user"); 1366 1367 return this.collaborate(accessibleByField, role, notify, canViewPath); 1368 } 1369 1370 /** 1371 * Used to retrieve all collaborations associated with the item. 1372 * 1373 * @param fields the optional fields to retrieve. 1374 * @return An iterable of metadata instances associated with the item. 1375 */ 1376 public BoxResourceIterable<BoxCollaboration.Info> getAllFileCollaborations(String... fields) { 1377 return BoxCollaboration.getAllFileCollaborations(this.getAPI(), this.getID(), 1378 GET_COLLABORATORS_PAGE_SIZE, fields); 1379 1380 } 1381 1382 /** 1383 * Contains information about a BoxFile. 1384 */ 1385 public class Info extends BoxItem.Info { 1386 private String sha1; 1387 private String versionNumber; 1388 private long commentCount; 1389 private EnumSet<Permission> permissions; 1390 private String extension; 1391 private boolean isPackage; 1392 private BoxFileVersion version; 1393 private URL previewLink; 1394 private BoxLock lock; 1395 private boolean isWatermarked; 1396 private JsonObject metadata; 1397 private Map<String, Map<String, Metadata>> metadataMap; 1398 private List<Representation> representations; 1399 1400 /** 1401 * Constructs an empty Info object. 1402 */ 1403 public Info() { 1404 super(); 1405 } 1406 1407 /** 1408 * Constructs an Info object by parsing information from a JSON string. 1409 * 1410 * @param json the JSON string to parse. 1411 */ 1412 public Info(String json) { 1413 super(json); 1414 } 1415 1416 /** 1417 * Constructs an Info object using an already parsed JSON object. 1418 * 1419 * @param jsonObject the parsed JSON object. 1420 */ 1421 public Info(JsonObject jsonObject) { 1422 super(jsonObject); 1423 } 1424 1425 @Override 1426 public BoxFile getResource() { 1427 return BoxFile.this; 1428 } 1429 1430 /** 1431 * Gets the SHA1 hash of the file. 1432 * 1433 * @return the SHA1 hash of the file. 1434 */ 1435 public String getSha1() { 1436 return this.sha1; 1437 } 1438 1439 /** 1440 * Gets the lock of the file. 1441 * 1442 * @return the lock of the file. 1443 */ 1444 public BoxLock getLock() { 1445 return this.lock; 1446 } 1447 1448 /** 1449 * Gets the current version number of the file. 1450 * 1451 * @return the current version number of the file. 1452 */ 1453 public String getVersionNumber() { 1454 return this.versionNumber; 1455 } 1456 1457 /** 1458 * Gets the number of comments on the file. 1459 * 1460 * @return the number of comments on the file. 1461 */ 1462 public long getCommentCount() { 1463 return this.commentCount; 1464 } 1465 1466 /** 1467 * Gets the permissions that the current user has on the file. 1468 * 1469 * @return the permissions that the current user has on the file. 1470 */ 1471 public EnumSet<Permission> getPermissions() { 1472 return this.permissions; 1473 } 1474 1475 /** 1476 * Gets the extension suffix of the file, excluding the dot. 1477 * 1478 * @return the extension of the file. 1479 */ 1480 public String getExtension() { 1481 return this.extension; 1482 } 1483 1484 /** 1485 * Gets whether or not the file is an OSX package. 1486 * 1487 * @return true if the file is an OSX package; otherwise false. 1488 */ 1489 public boolean getIsPackage() { 1490 return this.isPackage; 1491 } 1492 1493 /** 1494 * Gets the current version details of the file. 1495 * 1496 * @return the current version details of the file. 1497 */ 1498 public BoxFileVersion getVersion() { 1499 return this.version; 1500 } 1501 1502 /** 1503 * Gets the current expiring preview link. 1504 * 1505 * @return the expiring preview link 1506 */ 1507 public URL getPreviewLink() { 1508 return this.previewLink; 1509 } 1510 1511 /** 1512 * Gets flag indicating whether this file is Watermarked. 1513 * 1514 * @return whether the file is watermarked or not 1515 */ 1516 public boolean getIsWatermarked() { 1517 return this.isWatermarked; 1518 } 1519 1520 /** 1521 * Gets the metadata on this file associated with a specified scope and template. 1522 * Makes an attempt to get metadata that was retrieved using getInfo(String ...) method. If no result is found 1523 * then makes an API call to get metadata 1524 * @param templateName the metadata template type name. 1525 * @param scope the scope of the template (usually "global" or "enterprise"). 1526 * @return the metadata returned from the server. 1527 */ 1528 public Metadata getMetadata(String templateName, String scope) { 1529 try { 1530 return this.metadataMap.get(scope).get(templateName); 1531 } catch (NullPointerException e) { 1532 return null; 1533 } 1534 } 1535 1536 /** 1537 * Get file's representations. 1538 * @return list of representations 1539 */ 1540 public List<Representation> getRepresentations() { 1541 return this.representations; 1542 } 1543 1544 @Override 1545 protected void parseJSONMember(JsonObject.Member member) { 1546 super.parseJSONMember(member); 1547 1548 String memberName = member.getName(); 1549 JsonValue value = member.getValue(); 1550 if (memberName.equals("sha1")) { 1551 this.sha1 = value.asString(); 1552 } else if (memberName.equals("version_number")) { 1553 this.versionNumber = value.asString(); 1554 } else if (memberName.equals("comment_count")) { 1555 this.commentCount = value.asLong(); 1556 } else if (memberName.equals("permissions")) { 1557 this.permissions = this.parsePermissions(value.asObject()); 1558 } else if (memberName.equals("extension")) { 1559 this.extension = value.asString(); 1560 } else if (memberName.equals("is_package")) { 1561 this.isPackage = value.asBoolean(); 1562 } else if (memberName.equals("file_version")) { 1563 this.version = this.parseFileVersion(value.asObject()); 1564 } else if (memberName.equals("expiring_embed_link")) { 1565 try { 1566 String urlString = member.getValue().asObject().get("url").asString(); 1567 this.previewLink = new URL(urlString); 1568 } catch (MalformedURLException e) { 1569 throw new BoxAPIException("Couldn't parse expiring_embed_link/url for file", e); 1570 } 1571 } else if (memberName.equals("lock")) { 1572 if (value.isNull()) { 1573 this.lock = null; 1574 } else { 1575 this.lock = new BoxLock(value.asObject(), BoxFile.this.getAPI()); 1576 } 1577 } else if (memberName.equals("watermark_info")) { 1578 JsonObject jsonObject = value.asObject(); 1579 this.isWatermarked = jsonObject.get("is_watermarked").asBoolean(); 1580 } else if (memberName.equals("metadata")) { 1581 JsonObject jsonObject = value.asObject(); 1582 this.metadataMap = Parsers.parseAndPopulateMetadataMap(jsonObject); 1583 } else if (memberName.equals("representations")) { 1584 JsonObject jsonObject = value.asObject(); 1585 this.representations = Parsers.parseRepresentations(jsonObject); 1586 } 1587 } 1588 1589 private EnumSet<Permission> parsePermissions(JsonObject jsonObject) { 1590 EnumSet<Permission> permissions = EnumSet.noneOf(Permission.class); 1591 for (JsonObject.Member member : jsonObject) { 1592 JsonValue value = member.getValue(); 1593 if (value.isNull() || !value.asBoolean()) { 1594 continue; 1595 } 1596 1597 String memberName = member.getName(); 1598 if (memberName.equals("can_download")) { 1599 permissions.add(Permission.CAN_DOWNLOAD); 1600 } else if (memberName.equals("can_upload")) { 1601 permissions.add(Permission.CAN_UPLOAD); 1602 } else if (memberName.equals("can_rename")) { 1603 permissions.add(Permission.CAN_RENAME); 1604 } else if (memberName.equals("can_delete")) { 1605 permissions.add(Permission.CAN_DELETE); 1606 } else if (memberName.equals("can_share")) { 1607 permissions.add(Permission.CAN_SHARE); 1608 } else if (memberName.equals("can_set_share_access")) { 1609 permissions.add(Permission.CAN_SET_SHARE_ACCESS); 1610 } else if (memberName.equals("can_preview")) { 1611 permissions.add(Permission.CAN_PREVIEW); 1612 } else if (memberName.equals("can_comment")) { 1613 permissions.add(Permission.CAN_COMMENT); 1614 } 1615 } 1616 1617 return permissions; 1618 } 1619 1620 private BoxFileVersion parseFileVersion(JsonObject jsonObject) { 1621 return new BoxFileVersion(BoxFile.this.getAPI(), jsonObject, BoxFile.this.getID()); 1622 } 1623 } 1624 1625 /** 1626 * Enumerates the possible permissions that a user can have on a file. 1627 */ 1628 public enum Permission { 1629 /** 1630 * The user can download the file. 1631 */ 1632 CAN_DOWNLOAD("can_download"), 1633 1634 /** 1635 * The user can upload new versions of the file. 1636 */ 1637 CAN_UPLOAD("can_upload"), 1638 1639 /** 1640 * The user can rename the file. 1641 */ 1642 CAN_RENAME("can_rename"), 1643 1644 /** 1645 * The user can delete the file. 1646 */ 1647 CAN_DELETE("can_delete"), 1648 1649 /** 1650 * The user can share the file. 1651 */ 1652 CAN_SHARE("can_share"), 1653 1654 /** 1655 * The user can set the access level for shared links to the file. 1656 */ 1657 CAN_SET_SHARE_ACCESS("can_set_share_access"), 1658 1659 /** 1660 * The user can preview the file. 1661 */ 1662 CAN_PREVIEW("can_preview"), 1663 1664 /** 1665 * The user can comment on the file. 1666 */ 1667 CAN_COMMENT("can_comment"); 1668 1669 private final String jsonValue; 1670 1671 private Permission(String jsonValue) { 1672 this.jsonValue = jsonValue; 1673 } 1674 1675 static Permission fromJSONValue(String jsonValue) { 1676 return Permission.valueOf(jsonValue.toUpperCase()); 1677 } 1678 1679 String toJSONValue() { 1680 return this.jsonValue; 1681 } 1682 } 1683 1684}