js-datocms-client
js-datocms-client copied to clipboard
client side uploads: bring your own S3 url
Hey, Not really an issue, but more a suggestion this time.
I'm uploading files from the front end to my dato instance and don't expose any secrets to the client, by basically following https://community.datocms.com/t/uploading-images-from-forms-via-netlify-functions/640/3 with two api endpoints.
POST /get-upload-urlwhich accepts a file name and returns a signed S3 urlPUTthe file to S3 via the url returned from .1 (repeat that for every file, collect the paths, ids along the way)POST /createto finalize the submission which accepts the model data including the collected ids to tie it all together, create the uploads and items in dato.
apart from performing some funky dance with the server 💃🕺(which is fine) I ended up copying the uploadToS3 function because it already has built in progress and cancellation. I would love to see a convenient import { uploadToS3 } from 'datocms-client' export which I can use an bring my own S3 signed url.
From looking at it, this looks like a good entry point https://github.com/datocms/js-datocms-client/blob/master/src/upload/adapters/browser.js#L50
That seems doable, sure. Would it be OK to simply export the uploadToS3 function? https://github.com/datocms/js-datocms-client/blob/master/src/upload/adapters/browser.js#L50 is just some code that glues the creation of upload request with the upload itself.
See https://github.com/datocms/js-datocms-client/commit/ad8acadc5f1cbf0e2b27f21e1380e90030df1f55.
In browsers:
import { uploadToS3 } from 'datocms-client/lib/upload/adapters/browser';
On Node:
import { uploadToS3 } from 'datocms-client/lib/upload/adapters/node';
yeah, that's a good start and would remove duplication. I envisioned something which also takes care of the cancellation but I can also glue that together myself.
My Idea was something like below (untested, dummy code) but with a proper name :D
export const doIt = (uploadRequestPromise, file, { onProgress }) => {
let isCancelled = false;
let cancel = () => {
isCancelled = true;
};
const promise = uploadRequestPromise
.then(({ id, url }) => {
if (isCancelled) {
return Promise.reject(new Error('upload aborted'));
}
if (onProgress) {
onProgress({
type: 'uploadRequestComplete',
payload: {
id,
url,
},
});
}
const { promise: uploadPromise, cancel: cancelUpload } = uploadToS3(
file,
url,
{
onProgress,
},
);
cancel = cancelUpload;
return uploadPromise.then(() => id);
});
return {
promise,
cancel: () => {
cancel();
},
};
}
export default function browser(client, file, { onProgress, filename }) {
const uploadRequestPromise = client.uploadRequest.create({ filename: filename || file.name })
return doIt(uploadRequestPromise, file, { onProgress });
}