Skip to content

Commit

Permalink
feat(blob): Upgrade blob starter example to client uploads, progress,…
Browse files Browse the repository at this point in the history
… next 15 (#976)

This commit upgrades the blob starter example
(https://blob-starter.vercel.app/):
- Use client uploads
- Display upload progress instead of just loading dots
- Multiple UX fixes like reset button, correct handling of image/* only,
allow
  to upload another image once done
- Upgrade all packages
- Cleanup package.json
- Refactor code for easier reading

**Before**


https://github.com/user-attachments/assets/ccfc6d4b-773c-41db-9966-d9866ffa9c89

**After**


https://github.com/user-attachments/assets/6c00f4f7-f270-41dc-b767-e453433eb76d
  • Loading branch information
vvo authored Nov 14, 2024
1 parent c72c796 commit c90e40e
Show file tree
Hide file tree
Showing 9 changed files with 3,007 additions and 2,749 deletions.
3 changes: 0 additions & 3 deletions storage/blob-starter/.eslintrc.json

This file was deleted.

4 changes: 2 additions & 2 deletions storage/blob-starter/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"typescript.tsdk": "node_modules/.pnpm/typescript@5.0.4/node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
"typescript.enablePromptUseWorkspaceTsdk": true,
"typescript.tsdk": "node_modules/typescript/lib"
}
4 changes: 2 additions & 2 deletions storage/blob-starter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ relatedTemplates:

# Vercel Blob Next.js Starter

Simple Next.js template that uses [Vercel Blob](https://vercel.com/blob)for image uploads
Simple Next.js template that uses [Vercel Blob](https://vercel.com/blob) for image uploads

## Demo

https://blob-starter.vercel.app/
https://blob-starter.vercel.app

## How to Use

Expand Down
49 changes: 33 additions & 16 deletions storage/blob-starter/app/api/upload/route.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,38 @@
import { put } from '@vercel/blob'
import { handleUpload, type HandleUploadBody } from '@vercel/blob/client'
import { NextResponse } from 'next/server'
import { customAlphabet } from 'nanoid'

export const runtime = 'edge'
export async function POST(request: Request): Promise<NextResponse> {
const body = (await request.json()) as HandleUploadBody

const nanoid = customAlphabet(
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
7
) // 7-character random string
export async function POST(req: Request) {
const file = req.body || ''
const contentType = req.headers.get('content-type') || 'text/plain'
const filename = `${nanoid()}.${contentType.split('/')[1]}`
const blob = await put(filename, file, {
contentType,
access: 'public',
})
try {
const jsonResponse = await handleUpload({
body,
request,
onBeforeGenerateToken: async () =>
// pathname
// clientPayload
{
// Generate a client token for the browser to upload the file
// ⚠️ Authenticate and authorize users before generating the token.
// Otherwise, you're allowing anonymous uploads.
return {
allowedContentTypes: ['image/*'],
maximumSizeInBytes: 50 * 1024 * 1024, // 50MB
}
},
onUploadCompleted: async ({ blob, tokenPayload }) => {
// Get notified of client upload completion
// ⚠️ This will not work during development (localhost),
// Unless you use ngrok or a similar service to expose and test your local server
console.log('blob upload completed', blob, tokenPayload)
},
})

return NextResponse.json(blob)
return NextResponse.json(jsonResponse)
} catch (error) {
return NextResponse.json(
{ error: (error as Error).message },
{ status: 400 } // The webhook will retry 5 times waiting for a 200
)
}
}
13 changes: 0 additions & 13 deletions storage/blob-starter/components/loading-dots.tsx

This file was deleted.

12 changes: 12 additions & 0 deletions storage/blob-starter/components/progress-bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export default function ProgressBar({ value }: { value: number }) {
return (
<div className="relative pt-1">
<div className="flex h-2 mb-4 overflow-hidden text-xs bg-blue-200 rounded">
<div
style={{ width: `${value}%` }}
className="flex flex-col justify-center text-center text-white bg-blue-500 shadow-none whitespace-nowrap transition-all duration-500 ease-in-out"
/>
</div>
</div>
)
}
Loading

0 comments on commit c90e40e

Please sign in to comment.