Implement partial content support for GET: Add Content-Length and Content-Range headers (#9)

This commit is contained in:
Velih Dzen 2024-09-15 00:31:07 +08:00 committed by GitHub
parent 7503d6115b
commit 5f272ec046
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -133,36 +133,38 @@ async function handle_get(request: Request, bucket: R2Bucket): Promise<Response>
} else if (!isR2ObjectBody(object)) { } else if (!isR2ObjectBody(object)) {
return new Response('Precondition Failed', { status: 412 }); return new Response('Precondition Failed', { status: 412 });
} else { } else {
const { rangeOffset, rangeEnd } = calcContentRange(object);
const contentLength = rangeEnd - rangeOffset + 1;
return new Response(object.body, { return new Response(object.body, {
status: object.range ? 206 : 200, status: (object.range && contentLength !== object.size) ? 206 : 200,
headers: { headers: {
'Content-Type': object.httpMetadata?.contentType ?? 'application/octet-stream', 'Content-Type': object.httpMetadata?.contentType ?? 'application/octet-stream',
// TODO: Content-Length, Content-Range 'Content-Length': contentLength.toString(),
...({ 'Content-Range': `bytes ${rangeOffset}-${rangeEnd}/${object.size}` }),
...(object.httpMetadata?.contentDisposition ...(object.httpMetadata?.contentDisposition
? { ? {
'Content-Disposition': object.httpMetadata.contentDisposition, 'Content-Disposition': object.httpMetadata.contentDisposition,
} }
: {}), : {}),
...(object.httpMetadata?.contentEncoding ...(object.httpMetadata?.contentEncoding
? { ? {
'Content-Encoding': object.httpMetadata.contentEncoding, 'Content-Encoding': object.httpMetadata.contentEncoding,
} }
: {}), : {}),
...(object.httpMetadata?.contentLanguage ...(object.httpMetadata?.contentLanguage
? { ? {
'Content-Language': object.httpMetadata.contentLanguage, 'Content-Language': object.httpMetadata.contentLanguage,
} }
: {}), : {}),
...(object.httpMetadata?.cacheControl ...(object.httpMetadata?.cacheControl
? { ? {
'Cache-Control': object.httpMetadata.cacheControl, 'Cache-Control': object.httpMetadata.cacheControl,
} }
: {}), : {}),
...(object.httpMetadata?.cacheExpiry ...(object.httpMetadata?.cacheExpiry
? { ? {
'Cache-Expiry': object.httpMetadata.cacheExpiry.toISOString(), 'Cache-Expiry': object.httpMetadata.cacheExpiry.toISOString(),
} }
: {}), : {}),
}, },
}); });
@ -170,6 +172,24 @@ async function handle_get(request: Request, bucket: R2Bucket): Promise<Response>
} }
} }
function calcContentRange(object: R2ObjectBody) {
let rangeOffset = 0;
let rangeEnd = object.size - 1;
if (object.range) {
if ('suffix' in object.range) {
// Case 3: {suffix: number}
rangeOffset = object.size - object.range.suffix;
} else {
// Case 1: {offset: number, length?: number}
// Case 2: {offset?: number, length: number}
rangeOffset = object.range.offset ?? 0;
let length = object.range.length ?? (object.size - rangeOffset);
rangeEnd = Math.min(rangeOffset + length - 1, object.size - 1);
}
}
return { rangeOffset, rangeEnd };
}
async function handle_put(request: Request, bucket: R2Bucket): Promise<Response> { async function handle_put(request: Request, bucket: R2Bucket): Promise<Response> {
if (request.url.endsWith('/')) { if (request.url.endsWith('/')) {
return new Response('Method Not Allowed', { status: 405 }); return new Response('Method Not Allowed', { status: 405 });