Last active
November 14, 2024 09:13
-
-
Save SuaYoo/c6b7d0e17da9915cbdc171ab58152275 to your computer and use it in GitHub Desktop.
Uploading an image with Next.js and Blazeback B2 (AWS S3 alternative)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
BACKBLAZE_BUCKET_ID='YOUR_BACKBLAZE_BUCKET_ID' | |
BACKBLAZE_KEY_ID='YOUR_BACKBLAZE_KEY_ID' | |
BACKBLAZE_APP_KEY='YOUR_BACKBLAZE_APP_KEY' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import axios from 'axios'; | |
import { useState } from 'react'; | |
export default function ImageUpload() { | |
const [previewFile, setPreviewFile] = useState(); | |
const handleChange = (e) => { | |
const files = e.target.files; | |
const file = files[files.length - 1]; | |
if (file) { | |
// construct data uri for previewing the image | |
// before sending it to the server | |
const fileReader = new FileReader(); | |
fileReader.readAsDataURL(file); | |
fileReader.addEventListener('load', () => { | |
setPreviewFile({ | |
file, | |
base64: fileReader.result, | |
}); | |
}); | |
} else { | |
setPreviewFile(null); | |
} | |
}; | |
const upload = async () => { | |
const { file } = previewFile; | |
// rename the file before sending if you want | |
const fileExt = file.name.substring(file.name.lastIndexOf('.') + 1); | |
const fileName = `image.${fileExt}`; | |
try { | |
// pass the instance of File, not the base64 string, to the server | |
const { data } = await axios.post(`/api/media/upload`, file, { | |
headers: { | |
'content-type': file.type, | |
// TODO figure out how to parse file name from form data on the server | |
'x-filename': fileName, | |
}, | |
}); | |
// do something with the returned data, like store | |
// the URL returned from the server | |
} catch (err) { | |
console.error(err); | |
} | |
setPreviewFile(); | |
}; | |
return ( | |
<div> | |
<div> | |
{previewFile && <img src={previewFile.base64} />} | |
</div> | |
<input | |
type="file" | |
id="my-image-id" | |
name="my-image-id" | |
onChange={handleChange} | |
accept="image/png, image/jpeg" | |
/> | |
<button onClick={upload}>Submit</button> | |
</div> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import B2 from 'backblaze-b2'; | |
const uploadHandler = async (req, res) => { | |
// reconstruct file buffer from stream | |
const file = await new Promise((resolve) => { | |
const chunks = []; | |
req.on('readable', () => { | |
let chunk; | |
while (null !== (chunk = req.read())) { | |
chunks.push(chunk); | |
} | |
}); | |
req.on('end', () => { | |
resolve(Buffer.concat(chunks)); | |
}); | |
}); | |
const b2 = new B2({ | |
applicationKeyId: process.env.BACKBLAZE_KEY_ID, | |
applicationKey: process.env.BACKBLAZE_APP_KEY, | |
}); | |
// b2 auth tokens are valid for 24 hours | |
// .authorize returns the download url, | |
// .getUploadUrl returns the upload url and auth token | |
const { data: authData } = await b2.authorize(); | |
const { data: uploadData } = await b2.getUploadUrl({ | |
bucketId: process.env.BACKBLAZE_BUCKET_ID, | |
}); | |
// TODO figure out how to parse file name from form data | |
const reqFileName = req.headers['x-filename']; | |
const { data } = await b2.uploadFile({ | |
uploadUrl: uploadData.uploadUrl, | |
uploadAuthToken: uploadData.authorizationToken, | |
data: file, | |
// there are no real directories in b2, if you want to place | |
// your file in a folder structure, do so with slashes. ex: | |
// fileName: `/my-subfolder/uploads/${fileName}` | |
fileName, | |
// info: {}, // store optional info, like original file name | |
}); | |
// construct friendly url to return in the response | |
const bucketName = authData.allowed.bucketName; | |
const downloadURL = authData.downloadUrl; | |
res.status(200).json({ | |
// add timestamp to url to force re-fetching images with the same src | |
url: `${downloadURL}/file/${bucketName}/${data.fileName}?timestamp=${data.uploadTimestamp}`, | |
}); | |
}; | |
// tell next.js to disable body parsing and handle as a stream | |
export const config = { | |
api: { | |
bodyParser: false, | |
}, | |
}; | |
export default uploadHandler; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment