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