Supabase resumable uploads

Hey guys,

I saw another thread about using resumable upload. I’m wondering if anyone has had luck with this yet? In the documentation Supabase also mentions UppyJS (?)

I’m not actually looking for the actual resumable feature, but rather uploading large files with stability and getting the progress bar of uploads. I’m concerned that many of the files may be pretty large, and that it will be really hard to give users comfort during upload.

You could do this with the exposed SDK instance that WeWeb provides. I think there is many mentions about this, or at least I’ve mentioned it quite a lot before the big update that we had to the SDK v2.

Have you done it?
If so, were there any limitations, difficulties, things I should consider?

I probably have to search some more, but I didn’t quite grasp if anyone has had success with it.

I didn’t exactly do your usecase, but I’ve worked a lot with the SDK instance, with realtime and classic image uploads - before it even was a thing in the WeWeb’s plugin.

I’ll link you some resources that I can find on the forum :slight_smile:

I usually do these kind of things as a part of my coaching sessions, where we either crack it on the meeting, or I provide specific and aimed guidance regarding issues, as we have more time to discuss this in depth. You might want to book a session, just to try it out, usually people tell me they get quite deeper understanding of the topics we discuss, especially Supabase.

This one is your problem-specific:

Other references to using Supabase SDK’s instance:

I understand. I have no issues with Supabase Storage and WeWeb using regular storage. But, Supabase recommends any file over 6MB to use resumable upload (TUS); and limits standard uploads at 5GB.

I have to go down this path anyway, but I was wondering if anyone has done this before (or solved the 5GB limit or progress bar) in another way. I might just take you up on that call offer at some point.

Still looking for successful deployments of large file uploads and/or upload progress bar streaming with Supabase.

1 Like

With the above mentioned resources you should be able to use resumable uploads as per their Docs. So I’d definitely give it a try :slight_smile:

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

By the way, the same way you’d use the Supabase instance (via the wwLib) you also can go and change variables, and trigger workflows, without workarounds.

I can? I have done the variable change on success but I can’t trigger the component workflow? How do I access the workflow id?