Implement partial content support for GET: Add Content-Length and Content-Range headers
This commit is contained in:
parent
7503d6115b
commit
70b27972b9
46
src/index.ts
46
src/index.ts
@ -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 });
|
||||||
|
Loading…
Reference in New Issue
Block a user