@@ -354,9 +354,11 @@ class Client {
354354 endpoint : string ;
355355 endpointRealtime : string ;
356356 project : string ;
357+ key : string ;
357358 jwt : string ;
358359 locale : string ;
359360 session : string ;
361+ forwardeduseragent : string ;
360362 devkey : string ;
361363 cookie : string ;
362364 impersonateuserid : string ;
@@ -366,9 +368,11 @@ class Client {
366368 endpoint : 'https://cloud.appwrite.io/v1' ,
367369 endpointRealtime : '' ,
368370 project : '' ,
371+ key : '' ,
369372 jwt : '' ,
370373 locale : '' ,
371374 session : '' ,
375+ forwardeduseragent : '' ,
372376 devkey : '' ,
373377 cookie : '' ,
374378 impersonateuserid : '' ,
@@ -380,9 +384,9 @@ class Client {
380384 */
381385 headers : Headers = {
382386 'x-sdk-name' : 'Web' ,
383- 'x-sdk-platform' : 'client ' ,
387+ 'x-sdk-platform' : 'server ' ,
384388 'x-sdk-language' : 'web' ,
385- 'x-sdk-version' : '25.1.1 ' ,
389+ 'x-sdk-version' : '25.2.0 ' ,
386390 'X-Appwrite-Response-Format' : '1.9.5' ,
387391 } ;
388392
@@ -456,6 +460,20 @@ class Client {
456460 this . config . project = value ;
457461 return this ;
458462 }
463+ /**
464+ * Set Key
465+ *
466+ * Your secret API key
467+ *
468+ * @param value string
469+ *
470+ * @return {this }
471+ */
472+ setKey ( value : string ) : this {
473+ this . headers [ 'X-Appwrite-Key' ] = value ;
474+ this . config . key = value ;
475+ return this ;
476+ }
459477 /**
460478 * Set JWT
461479 *
@@ -496,6 +514,20 @@ class Client {
496514 this . config . session = value ;
497515 return this ;
498516 }
517+ /**
518+ * Set ForwardedUserAgent
519+ *
520+ * The user agent string of the client that made the request
521+ *
522+ * @param value string
523+ *
524+ * @return {this }
525+ */
526+ setForwardedUserAgent ( value : string ) : this {
527+ this . headers [ 'X-Forwarded-User-Agent' ] = value ;
528+ this . config . forwardeduseragent = value ;
529+ return this ;
530+ }
499531 /**
500532 * Set DevKey
501533 *
@@ -918,44 +950,131 @@ class Client {
918950 return await this . call ( method , url , headers , originalPayload ) ;
919951 }
920952
921- let start = 0 ;
922- let response = null ;
953+ const totalChunks = Math . ceil ( file . size / Client . CHUNK_SIZE ) ;
954+
955+ // Upload first chunk alone to get the upload ID
956+ const firstChunkEnd = Math . min ( Client . CHUNK_SIZE , file . size ) ;
957+ const firstChunkHeaders = { ...headers , 'content-range' : `bytes 0-${ firstChunkEnd - 1 } /${ file . size } ` } ;
958+ const firstChunk = file . slice ( 0 , firstChunkEnd ) ;
959+ const firstPayload = { ...originalPayload } ;
960+ firstPayload [ fileParam ] = new File ( [ firstChunk ] , file . name ) ;
961+
962+ let response = await this . call ( method , url , firstChunkHeaders , firstPayload ) ;
963+ const uploadId = response ?. $id ;
964+
965+ if ( onProgress && typeof onProgress === 'function' ) {
966+ onProgress ( {
967+ $id : uploadId ,
968+ progress : Math . round ( ( firstChunkEnd / file . size ) * 100 ) ,
969+ sizeUploaded : firstChunkEnd ,
970+ chunksTotal : totalChunks ,
971+ chunksUploaded : 1
972+ } ) ;
973+ }
923974
924- while ( start < file . size ) {
925- let end = start + Client . CHUNK_SIZE ; // Prepare end for the next chunk
926- if ( end >= file . size ) {
927- end = file . size ; // Adjust for the last chunk to include the last byte
928- }
975+ if ( totalChunks === 1 ) {
976+ return response ;
977+ }
978+
979+ // Prepare remaining chunks
980+ const chunks : { start : number ; end : number } [ ] = [ ] ;
981+ for ( let i = 1 ; i < totalChunks ; i ++ ) {
982+ const start = i * Client . CHUNK_SIZE ;
983+ const end = Math . min ( start + Client . CHUNK_SIZE , file . size ) ;
984+ chunks . push ( { start, end } ) ;
985+ }
929986
930- headers [ 'content-range' ] = `bytes ${ start } -${ end - 1 } /${ file . size } ` ;
931- const chunk = file . slice ( start , end ) ;
987+ // Upload remaining chunks with max concurrency of 8
988+ const CONCURRENCY = 8 ;
989+ let completedCount = 1 ;
990+ let uploadedBytes = firstChunkEnd ;
991+ let lastResponse = response ;
992+ let finalResponse = null ;
993+ let rejected = false ;
994+
995+ const isUploadComplete = ( chunkResponse : any ) => {
996+ const chunksUploaded = chunkResponse ?. chunksUploaded ;
997+ const chunksTotal = chunkResponse ?. chunksTotal ?? totalChunks ;
998+ return typeof chunksUploaded === 'number' && typeof chunksTotal === 'number' && chunksUploaded >= chunksTotal ;
999+ } ;
9321000
933- let payload = { ...originalPayload } ;
934- payload [ fileParam ] = new File ( [ chunk ] , file . name ) ;
1001+ const uploadChunk = async ( chunk : typeof chunks [ 0 ] ) => {
1002+ const chunkHeaders = { ...headers } ;
1003+ if ( uploadId ) {
1004+ chunkHeaders [ 'x-appwrite-id' ] = uploadId ;
1005+ }
1006+ chunkHeaders [ 'content-range' ] = `bytes ${ chunk . start } -${ chunk . end - 1 } /${ file . size } ` ;
1007+
1008+ const chunkBlob = file . slice ( chunk . start , chunk . end ) ;
1009+ const chunkPayload = { ...originalPayload } ;
1010+ chunkPayload [ fileParam ] = new File ( [ chunkBlob ] , file . name ) ;
9351011
936- response = await this . call ( method , url , headers , payload ) ;
1012+ const chunkResponse = await this . call ( method , url , chunkHeaders , chunkPayload ) ;
1013+
1014+ if ( rejected ) {
1015+ return chunkResponse ;
1016+ }
1017+
1018+ completedCount ++ ;
1019+ uploadedBytes += ( chunk . end - chunk . start ) ;
1020+
1021+ lastResponse = chunkResponse ;
1022+ if ( isUploadComplete ( chunkResponse ) ) {
1023+ finalResponse = chunkResponse ;
1024+ }
9371025
9381026 if ( onProgress && typeof onProgress === 'function' ) {
9391027 onProgress ( {
940- $id : response . $id ,
941- progress : Math . round ( ( end / file . size ) * 100 ) ,
942- sizeUploaded : end ,
943- chunksTotal : Math . ceil ( file . size / Client . CHUNK_SIZE ) ,
944- chunksUploaded : Math . ceil ( end / Client . CHUNK_SIZE )
1028+ $id : uploadId ,
1029+ progress : Math . round ( ( uploadedBytes / file . size ) * 100 ) ,
1030+ sizeUploaded : uploadedBytes ,
1031+ chunksTotal : totalChunks ,
1032+ chunksUploaded : completedCount
9451033 } ) ;
9461034 }
9471035
948- if ( response && response . $id ) {
949- headers [ 'x-appwrite-id' ] = response . $id ;
950- }
1036+ return chunkResponse ;
1037+ } ;
9511038
952- start = end ;
953- }
1039+ await new Promise < void > ( ( resolve , reject ) => {
1040+ let nextChunk = 0 ;
1041+ let inFlight = 0 ;
1042+ let completed = 0 ;
1043+
1044+ const uploadNext = ( ) => {
1045+ if ( rejected ) {
1046+ return ;
1047+ }
1048+
1049+ if ( completed === chunks . length ) {
1050+ resolve ( ) ;
1051+ return ;
1052+ }
1053+
1054+ while ( inFlight < CONCURRENCY && nextChunk < chunks . length ) {
1055+ const chunk = chunks [ nextChunk ++ ] ;
1056+ inFlight ++ ;
1057+
1058+ uploadChunk ( chunk )
1059+ . then ( ( ) => {
1060+ inFlight -- ;
1061+ completed ++ ;
1062+ uploadNext ( ) ;
1063+ } )
1064+ . catch ( ( error ) => {
1065+ rejected = true ;
1066+ reject ( error ) ;
1067+ } ) ;
1068+ }
1069+ } ;
1070+
1071+ uploadNext ( ) ;
1072+ } ) ;
9541073
955- return response ;
1074+ return finalResponse ?? lastResponse ;
9561075 }
9571076
958- async ping ( ) : Promise < string > {
1077+ async ping ( ) : Promise < unknown > {
9591078 return this . call ( 'GET' , new URL ( this . config . endpoint + '/ping' ) ) ;
9601079 }
9611080
0 commit comments