001package com.box.sdkgen.managers.chunkeduploads; 002 003import static com.box.sdkgen.internal.utils.UtilsManager.bufferLength; 004import static com.box.sdkgen.internal.utils.UtilsManager.convertToString; 005import static com.box.sdkgen.internal.utils.UtilsManager.entryOf; 006import static com.box.sdkgen.internal.utils.UtilsManager.generateByteStreamFromBuffer; 007import static com.box.sdkgen.internal.utils.UtilsManager.hexToBase64; 008import static com.box.sdkgen.internal.utils.UtilsManager.iterateChunks; 009import static com.box.sdkgen.internal.utils.UtilsManager.mapOf; 010import static com.box.sdkgen.internal.utils.UtilsManager.mergeMaps; 011import static com.box.sdkgen.internal.utils.UtilsManager.prepareParams; 012import static com.box.sdkgen.internal.utils.UtilsManager.readByteStream; 013import static com.box.sdkgen.internal.utils.UtilsManager.reduceIterator; 014 015import com.box.sdkgen.internal.utils.Hash; 016import com.box.sdkgen.internal.utils.HashName; 017import com.box.sdkgen.networking.auth.Authentication; 018import com.box.sdkgen.networking.fetchoptions.FetchOptions; 019import com.box.sdkgen.networking.fetchoptions.ResponseFormat; 020import com.box.sdkgen.networking.fetchresponse.FetchResponse; 021import com.box.sdkgen.networking.network.NetworkSession; 022import com.box.sdkgen.schemas.filefull.FileFull; 023import com.box.sdkgen.schemas.files.Files; 024import com.box.sdkgen.schemas.uploadedpart.UploadedPart; 025import com.box.sdkgen.schemas.uploadpart.UploadPart; 026import com.box.sdkgen.schemas.uploadparts.UploadParts; 027import com.box.sdkgen.schemas.uploadsession.UploadSession; 028import com.box.sdkgen.serialization.json.JsonManager; 029import java.io.InputStream; 030import java.util.Arrays; 031import java.util.Collections; 032import java.util.Iterator; 033import java.util.List; 034import java.util.Map; 035import java.util.stream.Collectors; 036import java.util.stream.Stream; 037 038public class ChunkedUploadsManager { 039 040 public Authentication auth; 041 042 public NetworkSession networkSession; 043 044 public ChunkedUploadsManager() { 045 this.networkSession = new NetworkSession(); 046 } 047 048 protected ChunkedUploadsManager(Builder builder) { 049 this.auth = builder.auth; 050 this.networkSession = builder.networkSession; 051 } 052 053 /** 054 * Creates an upload session for a new file. 055 * 056 * @param requestBody Request body of createFileUploadSession method 057 */ 058 public UploadSession createFileUploadSession(CreateFileUploadSessionRequestBody requestBody) { 059 return createFileUploadSession(requestBody, new CreateFileUploadSessionHeaders()); 060 } 061 062 /** 063 * Creates an upload session for a new file. 064 * 065 * @param requestBody Request body of createFileUploadSession method 066 * @param headers Headers of createFileUploadSession method 067 */ 068 public UploadSession createFileUploadSession( 069 CreateFileUploadSessionRequestBody requestBody, CreateFileUploadSessionHeaders headers) { 070 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 071 FetchResponse response = 072 this.networkSession 073 .getNetworkClient() 074 .fetch( 075 new FetchOptions.Builder( 076 String.join( 077 "", 078 this.networkSession.getBaseUrls().getUploadUrl(), 079 "/2.0/files/upload_sessions"), 080 "POST") 081 .headers(headersMap) 082 .data(JsonManager.serialize(requestBody)) 083 .contentType("application/json") 084 .responseFormat(ResponseFormat.JSON) 085 .auth(this.auth) 086 .networkSession(this.networkSession) 087 .build()); 088 return JsonManager.deserialize(response.getData(), UploadSession.class); 089 } 090 091 /** 092 * Creates an upload session for an existing file. 093 * 094 * @param fileId The unique identifier that represents a file. 095 * <p>The ID for any file can be determined by visiting a file in the web application and 096 * copying the ID from the URL. For example, for the URL `https://*.app.box.com/files/123` the 097 * `file_id` is `123`. Example: "12345" 098 * @param requestBody Request body of createFileUploadSessionForExistingFile method 099 */ 100 public UploadSession createFileUploadSessionForExistingFile( 101 String fileId, CreateFileUploadSessionForExistingFileRequestBody requestBody) { 102 return createFileUploadSessionForExistingFile( 103 fileId, requestBody, new CreateFileUploadSessionForExistingFileHeaders()); 104 } 105 106 /** 107 * Creates an upload session for an existing file. 108 * 109 * @param fileId The unique identifier that represents a file. 110 * <p>The ID for any file can be determined by visiting a file in the web application and 111 * copying the ID from the URL. For example, for the URL `https://*.app.box.com/files/123` the 112 * `file_id` is `123`. Example: "12345" 113 * @param requestBody Request body of createFileUploadSessionForExistingFile method 114 * @param headers Headers of createFileUploadSessionForExistingFile method 115 */ 116 public UploadSession createFileUploadSessionForExistingFile( 117 String fileId, 118 CreateFileUploadSessionForExistingFileRequestBody requestBody, 119 CreateFileUploadSessionForExistingFileHeaders headers) { 120 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 121 FetchResponse response = 122 this.networkSession 123 .getNetworkClient() 124 .fetch( 125 new FetchOptions.Builder( 126 String.join( 127 "", 128 this.networkSession.getBaseUrls().getUploadUrl(), 129 "/2.0/files/", 130 convertToString(fileId), 131 "/upload_sessions"), 132 "POST") 133 .headers(headersMap) 134 .data(JsonManager.serialize(requestBody)) 135 .contentType("application/json") 136 .responseFormat(ResponseFormat.JSON) 137 .auth(this.auth) 138 .networkSession(this.networkSession) 139 .build()); 140 return JsonManager.deserialize(response.getData(), UploadSession.class); 141 } 142 143 /** 144 * Using this method with urls provided in response when creating a new upload session is 145 * preferred to use over GetFileUploadSessionById method. This allows to always upload your 146 * content to the closest Box data center and can significantly improve upload speed. Return 147 * information about an upload session. 148 * 149 * <p>The actual endpoint URL is returned by the [`Create upload 150 * session`](https://developer.box.com/reference/post-files-upload-sessions) endpoint. 151 * 152 * @param url URL of getFileUploadSessionById method 153 */ 154 public UploadSession getFileUploadSessionByUrl(String url) { 155 return getFileUploadSessionByUrl(url, new GetFileUploadSessionByUrlHeaders()); 156 } 157 158 /** 159 * Using this method with urls provided in response when creating a new upload session is 160 * preferred to use over GetFileUploadSessionById method. This allows to always upload your 161 * content to the closest Box data center and can significantly improve upload speed. Return 162 * information about an upload session. 163 * 164 * <p>The actual endpoint URL is returned by the [`Create upload 165 * session`](https://developer.box.com/reference/post-files-upload-sessions) endpoint. 166 * 167 * @param url URL of getFileUploadSessionById method 168 * @param headers Headers of getFileUploadSessionById method 169 */ 170 public UploadSession getFileUploadSessionByUrl( 171 String url, GetFileUploadSessionByUrlHeaders headers) { 172 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 173 FetchResponse response = 174 this.networkSession 175 .getNetworkClient() 176 .fetch( 177 new FetchOptions.Builder(url, "GET") 178 .headers(headersMap) 179 .responseFormat(ResponseFormat.JSON) 180 .auth(this.auth) 181 .networkSession(this.networkSession) 182 .build()); 183 return JsonManager.deserialize(response.getData(), UploadSession.class); 184 } 185 186 /** 187 * Return information about an upload session. 188 * 189 * <p>The actual endpoint URL is returned by the [`Create upload 190 * session`](https://developer.box.com/reference/post-files-upload-sessions) endpoint. 191 * 192 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 193 */ 194 public UploadSession getFileUploadSessionById(String uploadSessionId) { 195 return getFileUploadSessionById(uploadSessionId, new GetFileUploadSessionByIdHeaders()); 196 } 197 198 /** 199 * Return information about an upload session. 200 * 201 * <p>The actual endpoint URL is returned by the [`Create upload 202 * session`](https://developer.box.com/reference/post-files-upload-sessions) endpoint. 203 * 204 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 205 * @param headers Headers of getFileUploadSessionById method 206 */ 207 public UploadSession getFileUploadSessionById( 208 String uploadSessionId, GetFileUploadSessionByIdHeaders headers) { 209 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 210 FetchResponse response = 211 this.networkSession 212 .getNetworkClient() 213 .fetch( 214 new FetchOptions.Builder( 215 String.join( 216 "", 217 this.networkSession.getBaseUrls().getUploadUrl(), 218 "/2.0/files/upload_sessions/", 219 convertToString(uploadSessionId)), 220 "GET") 221 .headers(headersMap) 222 .responseFormat(ResponseFormat.JSON) 223 .auth(this.auth) 224 .networkSession(this.networkSession) 225 .build()); 226 return JsonManager.deserialize(response.getData(), UploadSession.class); 227 } 228 229 /** 230 * Using this method with urls provided in response when creating a new upload session is 231 * preferred to use over UploadFilePart method. This allows to always upload your content to the 232 * closest Box data center and can significantly improve upload speed. Uploads a chunk of a file 233 * for an upload session. 234 * 235 * <p>The actual endpoint URL is returned by the [`Create upload 236 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 237 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 238 * 239 * @param url URL of uploadFilePart method 240 * @param requestBody Request body of uploadFilePart method 241 * @param headers Headers of uploadFilePart method 242 */ 243 public UploadedPart uploadFilePartByUrl( 244 String url, InputStream requestBody, UploadFilePartByUrlHeaders headers) { 245 Map<String, String> headersMap = 246 prepareParams( 247 mergeMaps( 248 mapOf( 249 entryOf("digest", convertToString(headers.getDigest())), 250 entryOf("content-range", convertToString(headers.getContentRange()))), 251 headers.getExtraHeaders())); 252 FetchResponse response = 253 this.networkSession 254 .getNetworkClient() 255 .fetch( 256 new FetchOptions.Builder(url, "PUT") 257 .headers(headersMap) 258 .fileStream(requestBody) 259 .contentType("application/octet-stream") 260 .responseFormat(ResponseFormat.JSON) 261 .auth(this.auth) 262 .networkSession(this.networkSession) 263 .build()); 264 return JsonManager.deserialize(response.getData(), UploadedPart.class); 265 } 266 267 /** 268 * Uploads a chunk of a file for an upload session. 269 * 270 * <p>The actual endpoint URL is returned by the [`Create upload 271 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 272 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 273 * 274 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 275 * @param requestBody Request body of uploadFilePart method 276 * @param headers Headers of uploadFilePart method 277 */ 278 public UploadedPart uploadFilePart( 279 String uploadSessionId, InputStream requestBody, UploadFilePartHeaders headers) { 280 Map<String, String> headersMap = 281 prepareParams( 282 mergeMaps( 283 mapOf( 284 entryOf("digest", convertToString(headers.getDigest())), 285 entryOf("content-range", convertToString(headers.getContentRange()))), 286 headers.getExtraHeaders())); 287 FetchResponse response = 288 this.networkSession 289 .getNetworkClient() 290 .fetch( 291 new FetchOptions.Builder( 292 String.join( 293 "", 294 this.networkSession.getBaseUrls().getUploadUrl(), 295 "/2.0/files/upload_sessions/", 296 convertToString(uploadSessionId)), 297 "PUT") 298 .headers(headersMap) 299 .fileStream(requestBody) 300 .contentType("application/octet-stream") 301 .responseFormat(ResponseFormat.JSON) 302 .auth(this.auth) 303 .networkSession(this.networkSession) 304 .build()); 305 return JsonManager.deserialize(response.getData(), UploadedPart.class); 306 } 307 308 /** 309 * Using this method with urls provided in response when creating a new upload session is 310 * preferred to use over DeleteFileUploadSessionById method. This allows to always upload your 311 * content to the closest Box data center and can significantly improve upload speed. Abort an 312 * upload session and discard all data uploaded. 313 * 314 * <p>This cannot be reversed. 315 * 316 * <p>The actual endpoint URL is returned by the [`Create upload 317 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 318 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 319 * 320 * @param url URL of deleteFileUploadSessionById method 321 */ 322 public void deleteFileUploadSessionByUrl(String url) { 323 deleteFileUploadSessionByUrl(url, new DeleteFileUploadSessionByUrlHeaders()); 324 } 325 326 /** 327 * Using this method with urls provided in response when creating a new upload session is 328 * preferred to use over DeleteFileUploadSessionById method. This allows to always upload your 329 * content to the closest Box data center and can significantly improve upload speed. Abort an 330 * upload session and discard all data uploaded. 331 * 332 * <p>This cannot be reversed. 333 * 334 * <p>The actual endpoint URL is returned by the [`Create upload 335 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 336 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 337 * 338 * @param url URL of deleteFileUploadSessionById method 339 * @param headers Headers of deleteFileUploadSessionById method 340 */ 341 public void deleteFileUploadSessionByUrl( 342 String url, DeleteFileUploadSessionByUrlHeaders headers) { 343 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 344 FetchResponse response = 345 this.networkSession 346 .getNetworkClient() 347 .fetch( 348 new FetchOptions.Builder(url, "DELETE") 349 .headers(headersMap) 350 .responseFormat(ResponseFormat.NO_CONTENT) 351 .auth(this.auth) 352 .networkSession(this.networkSession) 353 .build()); 354 } 355 356 /** 357 * Abort an upload session and discard all data uploaded. 358 * 359 * <p>This cannot be reversed. 360 * 361 * <p>The actual endpoint URL is returned by the [`Create upload 362 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 363 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 364 * 365 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 366 */ 367 public void deleteFileUploadSessionById(String uploadSessionId) { 368 deleteFileUploadSessionById(uploadSessionId, new DeleteFileUploadSessionByIdHeaders()); 369 } 370 371 /** 372 * Abort an upload session and discard all data uploaded. 373 * 374 * <p>This cannot be reversed. 375 * 376 * <p>The actual endpoint URL is returned by the [`Create upload 377 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 378 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 379 * 380 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 381 * @param headers Headers of deleteFileUploadSessionById method 382 */ 383 public void deleteFileUploadSessionById( 384 String uploadSessionId, DeleteFileUploadSessionByIdHeaders headers) { 385 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 386 FetchResponse response = 387 this.networkSession 388 .getNetworkClient() 389 .fetch( 390 new FetchOptions.Builder( 391 String.join( 392 "", 393 this.networkSession.getBaseUrls().getUploadUrl(), 394 "/2.0/files/upload_sessions/", 395 convertToString(uploadSessionId)), 396 "DELETE") 397 .headers(headersMap) 398 .responseFormat(ResponseFormat.NO_CONTENT) 399 .auth(this.auth) 400 .networkSession(this.networkSession) 401 .build()); 402 } 403 404 /** 405 * Using this method with urls provided in response when creating a new upload session is 406 * preferred to use over GetFileUploadSessionParts method. This allows to always upload your 407 * content to the closest Box data center and can significantly improve upload speed. Return a 408 * list of the chunks uploaded to the upload session so far. 409 * 410 * <p>The actual endpoint URL is returned by the [`Create upload 411 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 412 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 413 * 414 * @param url URL of getFileUploadSessionParts method 415 */ 416 public UploadParts getFileUploadSessionPartsByUrl(String url) { 417 return getFileUploadSessionPartsByUrl( 418 url, 419 new GetFileUploadSessionPartsByUrlQueryParams(), 420 new GetFileUploadSessionPartsByUrlHeaders()); 421 } 422 423 /** 424 * Using this method with urls provided in response when creating a new upload session is 425 * preferred to use over GetFileUploadSessionParts method. This allows to always upload your 426 * content to the closest Box data center and can significantly improve upload speed. Return a 427 * list of the chunks uploaded to the upload session so far. 428 * 429 * <p>The actual endpoint URL is returned by the [`Create upload 430 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 431 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 432 * 433 * @param url URL of getFileUploadSessionParts method 434 * @param queryParams Query parameters of getFileUploadSessionParts method 435 */ 436 public UploadParts getFileUploadSessionPartsByUrl( 437 String url, GetFileUploadSessionPartsByUrlQueryParams queryParams) { 438 return getFileUploadSessionPartsByUrl( 439 url, queryParams, new GetFileUploadSessionPartsByUrlHeaders()); 440 } 441 442 /** 443 * Using this method with urls provided in response when creating a new upload session is 444 * preferred to use over GetFileUploadSessionParts method. This allows to always upload your 445 * content to the closest Box data center and can significantly improve upload speed. Return a 446 * list of the chunks uploaded to the upload session so far. 447 * 448 * <p>The actual endpoint URL is returned by the [`Create upload 449 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 450 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 451 * 452 * @param url URL of getFileUploadSessionParts method 453 * @param headers Headers of getFileUploadSessionParts method 454 */ 455 public UploadParts getFileUploadSessionPartsByUrl( 456 String url, GetFileUploadSessionPartsByUrlHeaders headers) { 457 return getFileUploadSessionPartsByUrl( 458 url, new GetFileUploadSessionPartsByUrlQueryParams(), headers); 459 } 460 461 /** 462 * Using this method with urls provided in response when creating a new upload session is 463 * preferred to use over GetFileUploadSessionParts method. This allows to always upload your 464 * content to the closest Box data center and can significantly improve upload speed. Return a 465 * list of the chunks uploaded to the upload session so far. 466 * 467 * <p>The actual endpoint URL is returned by the [`Create upload 468 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 469 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 470 * 471 * @param url URL of getFileUploadSessionParts method 472 * @param queryParams Query parameters of getFileUploadSessionParts method 473 * @param headers Headers of getFileUploadSessionParts method 474 */ 475 public UploadParts getFileUploadSessionPartsByUrl( 476 String url, 477 GetFileUploadSessionPartsByUrlQueryParams queryParams, 478 GetFileUploadSessionPartsByUrlHeaders headers) { 479 Map<String, String> queryParamsMap = 480 prepareParams( 481 mapOf( 482 entryOf("offset", convertToString(queryParams.getOffset())), 483 entryOf("limit", convertToString(queryParams.getLimit())))); 484 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 485 FetchResponse response = 486 this.networkSession 487 .getNetworkClient() 488 .fetch( 489 new FetchOptions.Builder(url, "GET") 490 .params(queryParamsMap) 491 .headers(headersMap) 492 .responseFormat(ResponseFormat.JSON) 493 .auth(this.auth) 494 .networkSession(this.networkSession) 495 .build()); 496 return JsonManager.deserialize(response.getData(), UploadParts.class); 497 } 498 499 /** 500 * Return a list of the chunks uploaded to the upload session so far. 501 * 502 * <p>The actual endpoint URL is returned by the [`Create upload 503 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 504 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 505 * 506 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 507 */ 508 public UploadParts getFileUploadSessionParts(String uploadSessionId) { 509 return getFileUploadSessionParts( 510 uploadSessionId, 511 new GetFileUploadSessionPartsQueryParams(), 512 new GetFileUploadSessionPartsHeaders()); 513 } 514 515 /** 516 * Return a list of the chunks uploaded to the upload session so far. 517 * 518 * <p>The actual endpoint URL is returned by the [`Create upload 519 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 520 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 521 * 522 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 523 * @param queryParams Query parameters of getFileUploadSessionParts method 524 */ 525 public UploadParts getFileUploadSessionParts( 526 String uploadSessionId, GetFileUploadSessionPartsQueryParams queryParams) { 527 return getFileUploadSessionParts( 528 uploadSessionId, queryParams, new GetFileUploadSessionPartsHeaders()); 529 } 530 531 /** 532 * Return a list of the chunks uploaded to the upload session so far. 533 * 534 * <p>The actual endpoint URL is returned by the [`Create upload 535 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 536 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 537 * 538 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 539 * @param headers Headers of getFileUploadSessionParts method 540 */ 541 public UploadParts getFileUploadSessionParts( 542 String uploadSessionId, GetFileUploadSessionPartsHeaders headers) { 543 return getFileUploadSessionParts( 544 uploadSessionId, new GetFileUploadSessionPartsQueryParams(), headers); 545 } 546 547 /** 548 * Return a list of the chunks uploaded to the upload session so far. 549 * 550 * <p>The actual endpoint URL is returned by the [`Create upload 551 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 552 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 553 * 554 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 555 * @param queryParams Query parameters of getFileUploadSessionParts method 556 * @param headers Headers of getFileUploadSessionParts method 557 */ 558 public UploadParts getFileUploadSessionParts( 559 String uploadSessionId, 560 GetFileUploadSessionPartsQueryParams queryParams, 561 GetFileUploadSessionPartsHeaders headers) { 562 Map<String, String> queryParamsMap = 563 prepareParams( 564 mapOf( 565 entryOf("offset", convertToString(queryParams.getOffset())), 566 entryOf("limit", convertToString(queryParams.getLimit())))); 567 Map<String, String> headersMap = prepareParams(mergeMaps(mapOf(), headers.getExtraHeaders())); 568 FetchResponse response = 569 this.networkSession 570 .getNetworkClient() 571 .fetch( 572 new FetchOptions.Builder( 573 String.join( 574 "", 575 this.networkSession.getBaseUrls().getUploadUrl(), 576 "/2.0/files/upload_sessions/", 577 convertToString(uploadSessionId), 578 "/parts"), 579 "GET") 580 .params(queryParamsMap) 581 .headers(headersMap) 582 .responseFormat(ResponseFormat.JSON) 583 .auth(this.auth) 584 .networkSession(this.networkSession) 585 .build()); 586 return JsonManager.deserialize(response.getData(), UploadParts.class); 587 } 588 589 /** 590 * Using this method with urls provided in response when creating a new upload session is 591 * preferred to use over CreateFileUploadSessionCommit method. This allows to always upload your 592 * content to the closest Box data center and can significantly improve upload speed. Close an 593 * upload session and create a file from the uploaded chunks. 594 * 595 * <p>The actual endpoint URL is returned by the [`Create upload 596 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 597 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 598 * 599 * @param url URL of createFileUploadSessionCommit method 600 * @param requestBody Request body of createFileUploadSessionCommit method 601 * @param headers Headers of createFileUploadSessionCommit method 602 */ 603 public Files createFileUploadSessionCommitByUrl( 604 String url, 605 CreateFileUploadSessionCommitByUrlRequestBody requestBody, 606 CreateFileUploadSessionCommitByUrlHeaders headers) { 607 Map<String, String> headersMap = 608 prepareParams( 609 mergeMaps( 610 mapOf( 611 entryOf("digest", convertToString(headers.getDigest())), 612 entryOf("if-match", convertToString(headers.getIfMatch())), 613 entryOf("if-none-match", convertToString(headers.getIfNoneMatch()))), 614 headers.getExtraHeaders())); 615 FetchResponse response = 616 this.networkSession 617 .getNetworkClient() 618 .fetch( 619 new FetchOptions.Builder(url, "POST") 620 .headers(headersMap) 621 .data(JsonManager.serialize(requestBody)) 622 .contentType("application/json") 623 .responseFormat(ResponseFormat.JSON) 624 .auth(this.auth) 625 .networkSession(this.networkSession) 626 .build()); 627 if (convertToString(response.getStatus()).equals("202")) { 628 return null; 629 } 630 return JsonManager.deserialize(response.getData(), Files.class); 631 } 632 633 /** 634 * Close an upload session and create a file from the uploaded chunks. 635 * 636 * <p>The actual endpoint URL is returned by the [`Create upload 637 * session`](https://developer.box.com/reference/post-files-upload-sessions) and [`Get upload 638 * session`](https://developer.box.com/reference/get-files-upload-sessions-id) endpoints. 639 * 640 * @param uploadSessionId The ID of the upload session. Example: "D5E3F7A" 641 * @param requestBody Request body of createFileUploadSessionCommit method 642 * @param headers Headers of createFileUploadSessionCommit method 643 */ 644 public Files createFileUploadSessionCommit( 645 String uploadSessionId, 646 CreateFileUploadSessionCommitRequestBody requestBody, 647 CreateFileUploadSessionCommitHeaders headers) { 648 Map<String, String> headersMap = 649 prepareParams( 650 mergeMaps( 651 mapOf( 652 entryOf("digest", convertToString(headers.getDigest())), 653 entryOf("if-match", convertToString(headers.getIfMatch())), 654 entryOf("if-none-match", convertToString(headers.getIfNoneMatch()))), 655 headers.getExtraHeaders())); 656 FetchResponse response = 657 this.networkSession 658 .getNetworkClient() 659 .fetch( 660 new FetchOptions.Builder( 661 String.join( 662 "", 663 this.networkSession.getBaseUrls().getUploadUrl(), 664 "/2.0/files/upload_sessions/", 665 convertToString(uploadSessionId), 666 "/commit"), 667 "POST") 668 .headers(headersMap) 669 .data(JsonManager.serialize(requestBody)) 670 .contentType("application/json") 671 .responseFormat(ResponseFormat.JSON) 672 .auth(this.auth) 673 .networkSession(this.networkSession) 674 .build()); 675 if (convertToString(response.getStatus()).equals("202")) { 676 return null; 677 } 678 return JsonManager.deserialize(response.getData(), Files.class); 679 } 680 681 public PartAccumulator reducer(PartAccumulator acc, InputStream chunk) { 682 long lastIndex = acc.getLastIndex(); 683 List<UploadPart> parts = acc.getParts(); 684 byte[] chunkBuffer = readByteStream(chunk); 685 Hash hash = new Hash(HashName.SHA1); 686 hash.updateHash(chunkBuffer); 687 String sha1 = hash.digestHash("base64"); 688 String digest = String.join("", "sha=", sha1); 689 int chunkSize = bufferLength(chunkBuffer); 690 long bytesStart = lastIndex + 1; 691 long bytesEnd = lastIndex + chunkSize; 692 String contentRange = 693 String.join( 694 "", 695 "bytes ", 696 convertToString(bytesStart), 697 "-", 698 convertToString(bytesEnd), 699 "/", 700 convertToString(acc.getFileSize())); 701 UploadedPart uploadedPart = 702 this.uploadFilePartByUrl( 703 acc.getUploadPartUrl(), 704 generateByteStreamFromBuffer(chunkBuffer), 705 new UploadFilePartByUrlHeaders(digest, contentRange)); 706 UploadPart part = uploadedPart.getPart(); 707 String partSha1 = hexToBase64(part.getSha1()); 708 assert partSha1.equals(sha1); 709 assert part.getSize() == chunkSize; 710 assert part.getOffset() == bytesStart; 711 acc.getFileHash().updateHash(chunkBuffer); 712 return new PartAccumulator( 713 bytesEnd, 714 Stream.concat(parts.stream(), Arrays.asList(part).stream()).collect(Collectors.toList()), 715 acc.getFileSize(), 716 acc.getUploadPartUrl(), 717 acc.getFileHash()); 718 } 719 720 /** 721 * Starts the process of chunk uploading a big file. Should return a File object representing 722 * uploaded file. 723 * 724 * @param file The stream of the file to upload. 725 * @param fileName The name of the file, which will be used for storage in Box. 726 * @param fileSize The total size of the file for the chunked upload in bytes. 727 * @param parentFolderId The ID of the folder where the file should be uploaded. 728 */ 729 public FileFull uploadBigFile( 730 InputStream file, String fileName, long fileSize, String parentFolderId) { 731 UploadSession uploadSession = 732 this.createFileUploadSession( 733 new CreateFileUploadSessionRequestBody(parentFolderId, fileSize, fileName)); 734 String uploadPartUrl = uploadSession.getSessionEndpoints().getUploadPart(); 735 String commitUrl = uploadSession.getSessionEndpoints().getCommit(); 736 String listPartsUrl = uploadSession.getSessionEndpoints().getListParts(); 737 long partSize = uploadSession.getPartSize(); 738 int totalParts = uploadSession.getTotalParts(); 739 assert partSize * totalParts >= fileSize; 740 assert uploadSession.getNumPartsProcessed() == 0; 741 Hash fileHash = new Hash(HashName.SHA1); 742 Iterator<InputStream> chunksIterator = iterateChunks(file, partSize, fileSize); 743 PartAccumulator results = 744 reduceIterator( 745 chunksIterator, 746 this::reducer, 747 new PartAccumulator(-1, Collections.emptyList(), fileSize, uploadPartUrl, fileHash)); 748 List<UploadPart> parts = results.getParts(); 749 UploadParts processedSessionParts = this.getFileUploadSessionPartsByUrl(listPartsUrl); 750 assert processedSessionParts.getTotalCount() == totalParts; 751 String sha1 = fileHash.digestHash("base64"); 752 String digest = String.join("", "sha=", sha1); 753 Files committedSession = 754 this.createFileUploadSessionCommitByUrl( 755 commitUrl, 756 new CreateFileUploadSessionCommitByUrlRequestBody(parts), 757 new CreateFileUploadSessionCommitByUrlHeaders(digest)); 758 return committedSession.getEntries().get(0); 759 } 760 761 public Authentication getAuth() { 762 return auth; 763 } 764 765 public NetworkSession getNetworkSession() { 766 return networkSession; 767 } 768 769 public static class Builder { 770 771 protected Authentication auth; 772 773 protected NetworkSession networkSession; 774 775 public Builder() {} 776 777 public Builder auth(Authentication auth) { 778 this.auth = auth; 779 return this; 780 } 781 782 public Builder networkSession(NetworkSession networkSession) { 783 this.networkSession = networkSession; 784 return this; 785 } 786 787 public ChunkedUploadsManager build() { 788 if (this.networkSession == null) { 789 this.networkSession = new NetworkSession(); 790 } 791 return new ChunkedUploadsManager(this); 792 } 793 } 794}