Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(blob): allow folder creation #559

Merged
merged 6 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/blob/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ export type { PutBlobResult, PutCommandOptions } from './put';
*
* If you want to upload from the browser directly, check out the documentation for client uploads: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk#client-uploads
*
* @param pathname - The pathname to upload the blob to. This includes the filename.
* @param body - The contents of your blob. This has to be a supported fetch body type https://developer.mozilla.org/en-US/docs/Web/API/fetch#body.
* @param pathname - The pathname to upload the blob to. For file upload this includes the filename. Pathnames that end with a slash are treated as folder creations.
* @param body - The contents of your blob. This has to be either a supported fetch body type https://developer.mozilla.org/en-US/docs/Web/API/fetch#body or undefined for folder creations.
* @param options - Additional options like `token` or `contentType`.
*/
export const put = createPutMethod<PutCommandOptions>({
Expand Down
42 changes: 29 additions & 13 deletions packages/blob/src/put.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,42 @@ export function createPutMethod<
getToken?: (pathname: string, options: T) => Promise<string>;
extraChecks?: (options: T) => void;
}) {
return async function put(
pathname: string,
body:
| string
| Readable
| Blob
| ArrayBuffer
| FormData
| ReadableStream
| File,
options?: T,
return async function put<TPath extends string>(
pathname: TPath,
bodyOrOptions: TPath extends `${string}/`
? T
:
| string
| Readable
| Blob
| ArrayBuffer
| FormData
| ReadableStream
| File,
optionsInput?: T,
): Promise<PutBlobResult> {
if (!pathname) {
throw new BlobError('pathname is required');
}

if (!body) {
const isEmptyFolder = pathname.endsWith('/');
luismeyer marked this conversation as resolved.
Show resolved Hide resolved

// avoid using the options as body
const body = isEmptyFolder ? undefined : (bodyOrOptions as BodyInit);

// when no body is required options are the second argument
const options = isEmptyFolder ? (bodyOrOptions as T) : optionsInput;

// prevent empty bodies for files
if (!body && !isEmptyFolder) {
throw new BlobError('body is required');
}

// runtime check for non TS users that provide all three args
if (bodyOrOptions && optionsInput && isEmptyFolder) {
throw new BlobError('body is not allowed for creating empty folders');
}

if (!options) {
throw new BlobError('missing options, see usage');
}
Expand Down Expand Up @@ -105,7 +121,7 @@ export function createPutMethod<

const blobApiResponse = await fetch(getApiUrl(`/${pathname}`), {
method: 'PUT',
body: body as BodyInit,
body,
luismeyer marked this conversation as resolved.
Show resolved Hide resolved
headers,
// required in order to stream some body types to Cloudflare
// currently only supported in Node.js, we may have to feature detect this
Expand Down
13 changes: 13 additions & 0 deletions test/next/src/app/vercel/blob/script.mts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async function run(): Promise<void> {
weirdCharactersExample(),
copyTextFile(),
listFolders(),
createFolder(),
]);

await Promise.all(
Expand Down Expand Up @@ -291,3 +292,15 @@ async function listFolders() {

return blob.url;
}

async function createFolder() {
const start = Date.now();

const blob = await vercelBlob.put('foolder/file.txt', '', {
access: 'public',
});

console.log('create folder example:', blob, `(${Date.now() - start}ms)`);

return blob.url;
}
Loading