We are very close to getting this sorted but cannot get past an error where the upload starts successfully, progresses, then stops at say 11% - likely the end of the first chunk.
The uploader uses a worker.
The error is always: Upload failed: Error: tus: invalid or missing offset value, originated from request (method: HEAD, url: https://anglers-planet-video-uploader.fathomstudio.workers.dev/null, response code: 200, response text: , request id
Any help appreciated.
Here is the handler.ts file:
// Updated CORS headers to include more specific directives
const corsHeaders = {
"Access-Control-Allow-Origin": "*", // Allows requests from any origin
"Access-Control-Allow-Methods": "POST, GET, OPTIONS, DELETE, PUT, PATCH", // Specifies allowed request methods
"Access-Control-Allow-Headers":
"Content-Type, Authorization, Tus-Resumable, Upload-Length, Upload-Metadata, Location, Upload-Offset", // Explicitly lists allowed headers
"Access-Control-Expose-Headers": "Location, Upload-Offset", // Exposes all headers to the frontend
};
declare global {
const ACCOUNT_ID: string;
const AP_UPLOAD_TOKEN: string;
}
export async function handleBasicCreatorUpload(
request: Request
): Promise<Response> {
const result = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/stream/direct_upload`,
{
headers: {
Authorization: `Bearer ${AP_UPLOAD_TOKEN}`,
},
body: JSON.stringify({
maxDurationSeconds: 3600,
expiry: "2022-06-07T23:20:00Z",
requireSignedURLs: false,
allowedOrigins: ["*"],
}),
method: "POST",
}
);
return new Response(JSON.stringify(await result.json(), null, 2), {
headers: {
...corsHeaders,
"Content-Type": "application/json;charset=UTF-8",
},
});
}
export async function handleTusCreatorUpload(
request: Request
): Promise<Response> {
// Enhanced handling of OPTIONS request for CORS preflight
if (request.method === "OPTIONS" || request.method == "HEAD") {
return new Response(null, { headers: corsHeaders });
}
const result = await fetch(
`https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/stream?direct_user=true`,
{
headers: {
Authorization: `Bearer ${AP_UPLOAD_TOKEN}`,
"Tus-Resumable": "1.0.0",
"Upload-Length": request.headers.get("Upload-Length"),
"Upload-Metadata": request.headers.get("Upload-Metadata"),
},
method: "POST",
}
);
const destination = result.headers.get("Location");
return new Response(null, {
headers: {
...corsHeaders,
'Location': destination, // Ensuring the Location header is correctly set for TUS uploads
},
});
}
export async function handleCreatorUpload(request: Request): Promise<Response> {
// Implementation of this function remains as per your specific requirements
return null; // Placeholder return statement
}
Here is tusUpload.js:
const getExpiryDate = () => {
let theDate = new Date()
theDate.setHours(theDate.getHours() + 5)
return theDate.toISOString()
}
function startUpload(file, chunkSize, endpoint) {
const options = {
endpoint: endpoint,
chunkSize: chunkSize,
metadata: {
expiry: getExpiryDate(),
maxDurationSeconds: 3600,
name: file.name,
},
onError(error) {
console.error('Upload failed:', error)
},
onSuccess() {
console.log('Upload finished successfully.')
},
onProgress(bytesUploaded, bytesTotal) {
const progressPercentage = (bytesUploaded / bytesTotal) * 100
console.log(`Upload progress: ${progressPercentage.toFixed(2)}%`)
const progressBar = document.getElementById('progress-bar-fill')
const progressText = document.getElementById('progress-bar-percent')
progressBar.style.width = progressPercentage + '%'
progressText.innerText = `Progress: ${progressPercentage.toFixed(2)}%`
},
}
let tusUpload = new tus.Upload(file, options)
tusUpload.start()
}
``
Here is the index.html:
Cloudflare TUS Uploader
<!-- External Scripts -->
<script src="https://cdn.jsdelivr.net/npm/tus-js-client@latest/dist/tus.min.js"></script>
<script src="./scripts/tusUpload.js"></script>
<!-- Optional Styles -->
<link rel="stylesheet" href="https://unpkg.com/mvp.css" />
<link rel="stylesheet" href="./styles/cf.css" />
Cloudflare Stream Tus Uploader
Select Video You Want To Upload
Upload Video With Tus
<script>
const upload = document.getElementById('upload')
upload.addEventListener('click', (e) => {
e.preventDefault()
const selectFile = document.getElementById('file')
startUpload(
selectFile.files[0],
5 * 1024 * 1024,
'https://anglers-planet-video-uploader.fathomstudio.workers.dev/upload',
)
})
</script>
```