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-url
which accepts a file name and returns a signed S3 url -
PUT
the file to S3 via the url returned from .1 (repeat that for every file, collect the paths, ids along the way) -
POST /create
to 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 });
}