-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update example for image upload with clearance of storage and cdn fil…
…es on every 5 minutes
- Loading branch information
1 parent
57db914
commit 2899b2b
Showing
10 changed files
with
291 additions
and
127 deletions.
There are no files selected for viewing
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
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
This file was deleted.
Oops, something went wrong.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
--- | ||
import Layout from "@layouts/Layout.astro"; | ||
import { listStorageRecords } from "@services/database"; | ||
const cache = Astro.locals.runtime.env.CACHE; | ||
let cachedStorageRecords = await cache.get("storage_records"); | ||
if (!cachedStorageRecords) { | ||
const storageRecords = await listStorageRecords(Astro.locals.dbClient); | ||
await cache.put("storage_records", JSON.stringify(storageRecords)); | ||
cachedStorageRecords = JSON.stringify(storageRecords); | ||
} | ||
const records = JSON.parse(cachedStorageRecords) as Awaited< | ||
ReturnType<typeof listStorageRecords> | ||
>; | ||
--- | ||
|
||
<Layout> | ||
<div class="container"> | ||
<div class="text-center"> | ||
<h1>The Gallery</h1> | ||
<p class="alert">The gallery resets in every 5 minutes!</p> | ||
<p><a href="/upload">Click here to upload new picture →</a></p> | ||
</div> | ||
<div class="masonry"> | ||
{ | ||
records.map((record) => ( | ||
<img src={`/cdn/${record.key}`} alt={record.originalName} /> | ||
)) | ||
} | ||
</div> | ||
</div> | ||
</Layout> | ||
|
||
<style> | ||
a { | ||
color: #6100ee; | ||
font-weight: bold; | ||
} | ||
.container { | ||
padding: 2rem; | ||
} | ||
.text-center { | ||
text-align: center; | ||
margin-bottom: 2rem; | ||
} | ||
.alert { | ||
color: maroon; | ||
font-weight: bold; | ||
} | ||
.masonry { | ||
column-count: 1; /* Default: 1 column on very small screens */ | ||
column-gap: 1rem; /* Gutter size */ | ||
} | ||
|
||
/* Make images fill the column’s width, with bottom margin as vertical spacing */ | ||
.masonry img { | ||
width: 100%; | ||
display: block; | ||
margin-bottom: 1rem; | ||
border-radius: 4px; /* optional styling */ | ||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); | ||
} | ||
|
||
/* At >= 600px: 2 columns */ | ||
@media (min-width: 600px) { | ||
.masonry { | ||
column-count: 2; | ||
} | ||
} | ||
|
||
/* At >= 900px: 3 columns */ | ||
@media (min-width: 900px) { | ||
.masonry { | ||
column-count: 3; | ||
} | ||
} | ||
|
||
/* At >= 1200px: 4 columns */ | ||
@media (min-width: 1200px) { | ||
.masonry { | ||
column-count: 4; | ||
} | ||
} | ||
|
||
/* At >= 1500px: 5 columns */ | ||
@media (min-width: 1500px) { | ||
.masonry { | ||
column-count: 5; | ||
} | ||
} | ||
</style> |
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
--- | ||
import Layout from "@layouts/Layout.astro"; | ||
import { handleFile } from "../utils/upload.util"; | ||
const MAX_FILE_SIZE = 1024 * 1024 * 5; // 5MB | ||
if (Astro.request.method === "POST") { | ||
try { | ||
const data = await Astro.request.formData(); | ||
const image = data.get("image"); | ||
if (image instanceof File) { | ||
if (!image.type.startsWith("image/")) { | ||
throw new Error("Invalid file type"); | ||
} | ||
if (image.size > MAX_FILE_SIZE) { | ||
throw new Error("File size too large"); | ||
} | ||
const fileData = await handleFile(image, Astro.locals); | ||
console.log(fileData); | ||
return Astro.redirect("/gallery"); | ||
} | ||
} catch { | ||
return Astro.redirect("/error"); | ||
} | ||
} | ||
--- | ||
|
||
<Layout> | ||
<div class="center"> | ||
<form method="post" enctype="multipart/form-data" action=""> | ||
<label class="upload-box" for="file"> | ||
<div class="show-on-submit loader"></div> | ||
<div class="hide-on-submit"> | ||
<span>Click to Upload</span> | ||
Upload a picture to the gallery | ||
<input type="file" id="file" name="image" accept="image/*" /> | ||
</div> | ||
</label> | ||
</form> | ||
<div class="hide-on-submit view-gallery"> | ||
<a href="/gallery">Or click to view the Gallery</a> | ||
</div> | ||
</div> | ||
</Layout> | ||
<script is:inline define:vars={{ MAX_FILE_SIZE }}> | ||
document | ||
.querySelector("input[type=file]") | ||
?.addEventListener?.("change", (e) => { | ||
if (e?.target instanceof HTMLInputElement) { | ||
const form = e.target.closest("form"); | ||
if (e.target instanceof File) { | ||
if (!e.target.type.startsWith("image/")) { | ||
alert("Only images are allowed"); | ||
form?.reset(); | ||
return; | ||
} | ||
|
||
if (e.target.size > MAX_FILE_SIZE) { | ||
alert("File size too large. Max 5MB allowed"); | ||
form?.reset(); | ||
return; | ||
} | ||
} | ||
if (form) { | ||
form.disabled = true; | ||
form.classList.add("loading"); | ||
form.submit(); | ||
} | ||
} | ||
}); | ||
</script> | ||
<style> | ||
h1 { | ||
margin: 0 0 1rem 0; | ||
padding: 0; | ||
font-size: 1.5rem; | ||
} | ||
.center { | ||
box-sizing: border-box; | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 100vh; /* Full viewport height */ | ||
flex-direction: column; | ||
padding: 10px; | ||
text-align: center; | ||
background-color: #f7f7f7; /* optional background color */ | ||
} | ||
|
||
/* The clickable box (label) for file upload */ | ||
.upload-box { | ||
display: block; | ||
border: 2px dashed #ccc; | ||
border-radius: 10px; | ||
width: 300px; /* fixed width so it doesn’t shrink too much */ | ||
padding: 2rem; | ||
font-size: 1.2rem; | ||
color: #555; | ||
background-color: #fff; | ||
cursor: pointer; | ||
transition: | ||
border-color 0.2s ease-in-out, | ||
background 0.2s ease-in-out; | ||
} | ||
|
||
/* Hover/focus states */ | ||
.upload-box:hover { | ||
border-color: #6100ee; | ||
background-color: #f0f8ff; /* light highlight */ | ||
} | ||
.upload-box span { | ||
display: block; | ||
font-size: 1.5rem; | ||
font-weight: bold; | ||
margin-bottom: 1rem; | ||
} | ||
|
||
/* Visually hide the real file input */ | ||
.upload-box input[type="file"] { | ||
display: none; | ||
} | ||
.view-gallery { | ||
margin-top: 1rem; | ||
} | ||
.view-gallery a { | ||
color: #6100ee; | ||
text-decoration: none; | ||
} | ||
|
||
.loader { | ||
height: 4px; | ||
width: 100%; | ||
--c: no-repeat linear-gradient(#6100ee 0 0); | ||
background: var(--c), var(--c), #d7b8fc; | ||
background-size: 60% 100%; | ||
animation: l16 3s infinite; | ||
} | ||
|
||
.loading .hide-on-submit { | ||
display: none; | ||
} | ||
|
||
.show-on-submit { | ||
display: none; | ||
} | ||
.loading .show-on-submit { | ||
display: block; | ||
} | ||
|
||
@keyframes l16 { | ||
0% { | ||
background-position: | ||
-150% 0, | ||
-150% 0; | ||
} | ||
66% { | ||
background-position: | ||
250% 0, | ||
-150% 0; | ||
} | ||
100% { | ||
background-position: | ||
250% 0, | ||
250% 0; | ||
} | ||
} | ||
</style> |
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
Oops, something went wrong.