@@ -17,6 +17,48 @@ export async function GET(req: NextRequest) {
1717 } catch {
1818 return makeAPIErrorResponse ( 400 , "Invalid \"url\" query parameter." )
1919 }
20- const file = await fetch ( url ) . then ( r => r . blob ( ) )
21- return new NextResponse ( file , { status : 200 } )
20+ try {
21+ const maxBytes = 10 * 1024 * 1024 // 10 MB
22+ const file = await downloadFile ( { url, maxBytes } )
23+ console . log ( "Downloaded" )
24+ return new NextResponse ( file , { status : 200 } )
25+ } catch ( error ) {
26+ if ( error instanceof Error == false ) {
27+ return makeAPIErrorResponse ( 500 , "An unknown error occurred." )
28+ }
29+ if ( error . name === "AbortError" ) {
30+ return makeAPIErrorResponse ( 413 , "The operation was aborted." )
31+ } else if ( error . name === "TimeoutError" ) {
32+ return makeAPIErrorResponse ( 408 , "The operation timed out." )
33+ } else {
34+ return makeAPIErrorResponse ( 500 , error . message )
35+ }
36+ }
37+ }
38+
39+ async function downloadFile ( params : { url : URL , maxBytes : number } ) : Promise < Blob > {
40+ const { url, maxBytes } = params
41+ const abortController = new AbortController ( )
42+ const response = await fetch ( url , {
43+ signal : AbortSignal . any ( [ abortController . signal ] )
44+ } )
45+ if ( ! response . body ) {
46+ throw new Error ( "Response body unavailable" )
47+ }
48+ let totalBytes = 0
49+ const reader = response . body . getReader ( )
50+ // eslint-disable-next-line no-constant-condition
51+ while ( true ) {
52+ // eslint-disable-next-line no-await-in-loop
53+ const { done, value } = await reader . read ( )
54+ if ( done ) {
55+ break
56+ }
57+ totalBytes += value . length
58+ if ( totalBytes >= maxBytes ) {
59+ abortController . abort ( )
60+ break
61+ }
62+ }
63+ return await response . blob ( )
2264}
0 commit comments