Supabase resumable uploads

I successfully implemented TUS.

I load this script on a global app workflow (thanks https://weweb-embed.statechange.ai/):

  if(!window["scp-loading-fe9fdf6a-9c05-4016-8f9c-62ea62b08990"]) {
    window["scp-loading-fe9fdf6a-9c05-4016-8f9c-62ea62b08990"] = true;
    let doc;
    /* Adding script from https://cdn.jsdelivr.net/npm/tus-js-client@latest/dist/tus.js */
    doc = document.createElement('script');
    doc.src = 'https://cdn.jsdelivr.net/npm/tus-js-client@latest/dist/tus.js';
    document.body.appendChild(doc);
  }

Then i run this on submit. It’s a lot of logging, but i had some problems i had to troubleshoot.
I’m also checking if the script is loaded, and if not, i load it. Not sure if needed, but ChatGPT and I did it anyway. I had some issues where I had to manually load the package.

I’m using the supabase auth session token to authenticate as well. Hopefully its secure enough.

const projectId = '**SUPABASE PROJECT ID**';

// Importing tus.js library asynchronously
(function() {
	if (!window["scp-loading-fe9fdf6a-9c05-4016-8f9c-62ea62b08990"]) {
		window["scp-loading-fe9fdf6a-9c05-4016-8f9c-62ea62b08990"] = true;
		let script = document.createElement('script');
		script.src = 'https://cdn.jsdelivr.net/npm/tus-js-client@4.1.0/lib.es5/browser/index.min.js';
		script.onload = function() {
			startUpload();
		};
		document.body.appendChild(script);
	} else {
		startUpload();
	}
})();

async function uploadFile(bucketName, filePath, fileName, file) {
	console.log('Starting upload process...');
	const {
		data: {
			session
		}
	} = await wwLib.wwPlugins.supabaseAuth.publicInstance.auth.getSession();
	console.log('Session retrieved:', session);

	return new Promise((resolve, reject) => {
		console.log('Creating tus upload...');
		var upload = new tus.Upload(file, {
			endpoint: `https://${projectId}.supabase.co/storage/v1/upload/resumable`,
			retryDelays: [0, 3000, 5000, 10000, 20000],
			headers: {
				authorization: `Bearer ${session.access_token}`,
				'x-upsert': 'true',
			},
			uploadDataDuringCreation: true,
			removeFingerprintOnSuccess: true,
			metadata: {
				bucketName: bucketName,
				objectName: fileName,
				contentType: file.type,
				cacheControl: '3600',
			},
			chunkSize: 6 * 1024 * 1024,
			onError: function(error) {
				console.log('Upload failed:', error);
				reject(error);
			},
			onProgress: function(bytesUploaded, bytesTotal) {
				var percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(0);
				console.log('Upload progress:', percentage + '%');

				// Update progress to context variables if it exists
				if (context.component && context.component.variables && context.component.variables['**SOMEWHERE TO DISPLAY %**'] !== undefined) {
					context.component.variables['**SOMEWHERE TO DISPLAY %**'] = percentage;
				} else {
					console.warn('Progress variable not found in context');
				}
			},
			onSuccess: function() {
				console.log('Download %s from %s', upload.file.name, upload.url)
				resolve()
			},
		});

		upload.findPreviousUploads().then(function(previousUploads) {
			console.log('Previous uploads found:', previousUploads);
			if (previousUploads.length) {
				upload.resumeFromPreviousUpload(previousUploads[0]);
			}
			console.log('Starting the upload...');
			upload.start();
		}).catch((error) => {
			console.log('Error finding previous uploads:', error);
			reject(error);
		});
	});
}

function startUpload() {
	console.log('Starting upload script...');
	const file = '**YOUR ACTUAL FILE**';
	if (!file) {
		console.error('No file found in context variables');
		return;
	}


	const filePath = `**CONSTRUCT FILE PATH**`;

	const bucketName = '**YOUR BUCKET NAME**';
	const fileName = `**YOUR FILE PATH + FILE NAME.EXT**`;

	uploadFile(bucketName, filePath, fileName, file).then(() => {
		console.log('File uploaded successfully');

		};
	}).catch((error) => {
		console.error('File upload failed', error);
	});
}

Additionally, you can trigger ie. a variable change or something onSuccess. I’m doing a variable change to true, and triggering a new workflow (with solution from @MatthewS Triggering Component Workflow w/ JS Workaround - #2 by Doc )

3 Likes