@@ -47,7 +47,7 @@ export class Upload extends EventEmitter {
4747
4848 // Defaults.
4949 private readonly queueSize : number = 4 ;
50- private readonly partSize = Upload . MIN_PART_SIZE ;
50+ private readonly partSize : number ;
5151 private readonly leavePartsOnError : boolean = false ;
5252 private readonly tags : Tag [ ] = [ ] ;
5353
@@ -66,6 +66,7 @@ export class Upload extends EventEmitter {
6666
6767 private uploadedParts : CompletedPart [ ] = [ ] ;
6868 private uploadEnqueuedPartsCount = 0 ;
69+ private expectedPartsCount = 0 ;
6970 /**
7071 * Last UploadId if the upload was done with MultipartUpload and not PutObject.
7172 */
@@ -81,19 +82,21 @@ export class Upload extends EventEmitter {
8182
8283 // set defaults from options.
8384 this . queueSize = options . queueSize || this . queueSize ;
84- this . partSize = options . partSize || this . partSize ;
8585 this . leavePartsOnError = options . leavePartsOnError || this . leavePartsOnError ;
8686 this . tags = options . tags || this . tags ;
8787
8888 this . client = options . client ;
8989 this . params = options . params ;
9090
91- this . __validateInput ( ) ;
92-
9391 // set progress defaults
9492 this . totalBytes = byteLength ( this . params . Body ) ;
9593 this . bytesUploadedSoFar = 0 ;
9694 this . abortController = options . abortController ?? new AbortController ( ) ;
95+
96+ this . partSize = this . __calculatePartSize ( this . totalBytes ?? 0 , Upload . MIN_PART_SIZE ) ;
97+ this . expectedPartsCount = this . totalBytes ? Math . ceil ( this . totalBytes / this . partSize ) : 1 ;
98+
99+ this . __validateInput ( ) ;
97100 }
98101
99102 async abort ( ) : Promise < void > {
@@ -234,6 +237,8 @@ export class Upload extends EventEmitter {
234237 return ;
235238 }
236239
240+ this . __validateUploadPart ( dataPart , this . partSize ) ;
241+
237242 // Use put instead of multipart for one chunk uploads.
238243 if ( dataPart . partNumber === 1 && dataPart . lastPart ) {
239244 return await this . __uploadUsingPut ( dataPart ) ;
@@ -364,6 +369,12 @@ export class Upload extends EventEmitter {
364369
365370 let result ;
366371 if ( this . isMultiPart ) {
372+ if ( this . totalBytes && this . uploadedParts . length !== this . expectedPartsCount ) {
373+ throw new Error (
374+ `Expected ${ this . expectedPartsCount } number of parts but uploaded only ${ this . uploadedParts . length } parts`
375+ ) ;
376+ }
377+
367378 this . uploadedParts . sort ( ( a , b ) => a . PartNumber ! - b . PartNumber ! ) ;
368379
369380 const uploadCompleteParams = {
@@ -427,6 +438,31 @@ export class Upload extends EventEmitter {
427438 } ) ;
428439 }
429440
441+ private __calculatePartSize ( targetPartSizeBytes : number , minPartSize : number ) : number {
442+ const calculatedPartSize = Math . floor ( targetPartSizeBytes / this . MAX_PARTS ) ;
443+ return Math . max ( minPartSize , calculatedPartSize ) ;
444+ }
445+
446+ private __validateUploadPart ( dataPart : RawDataPart , partSize : number ) : void {
447+ const actualPartSize = byteLength ( dataPart . data ) || 0 ;
448+
449+ // Skip validation for single-part uploads (PUT operations)
450+ if ( dataPart . partNumber === 1 && dataPart . lastPart ) {
451+ return ;
452+ }
453+
454+ if ( actualPartSize === 0 ) {
455+ throw new Error ( `Content length is missing on the data for part number ${ dataPart . partNumber } ` ) ;
456+ }
457+
458+ // Validate part size (last part may be smaller)
459+ if ( ! dataPart . lastPart && actualPartSize !== partSize ) {
460+ throw new Error (
461+ `The Part size for part number ${ dataPart . partNumber } , size ${ actualPartSize } does not match expected size ${ partSize } `
462+ ) ;
463+ }
464+ }
465+
430466 private __validateInput ( ) : void {
431467 if ( ! this . params ) {
432468 throw new Error ( `InputError: Upload requires params to be passed to upload.` ) ;
0 commit comments