feat: upload file nella cartella IMAGES_DIR

POST /upload_to_folder: sanitizza nome, valida estensione e contenuto
via cv2.imdecode, auto-rename su collisione.
Toolbar UI: bottone 'Carica file', dopo upload ricarica picker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-24 14:45:16 +02:00
parent cc7d035f66
commit 3e4c20ecf5
3 changed files with 81 additions and 5 deletions
+34 -5
View File
@@ -82,6 +82,21 @@ async function fetchImagesList() {
return await r.json();
}
async function uploadToFolder(file) {
const fd = new FormData();
fd.append("file", file);
const r = await fetch("/upload_to_folder", { method: "POST", body: fd });
if (!r.ok) throw new Error(await r.text());
return await r.json();
}
async function refreshPickers() {
const {files, dir} = await fetchImagesList();
buildThumbPicker("picker-model", files, onSelectModel);
buildThumbPicker("picker-scene", files, onSelectScene);
return {files, dir};
}
function buildThumbPicker(pickerId, files, onSelect) {
const picker = document.getElementById(pickerId);
const current = picker.querySelector(".picker-current");
@@ -351,14 +366,28 @@ window.addEventListener("DOMContentLoaded", async () => {
buildAdvancedForm();
setupROI();
// Popola picker immagini da IMAGES_DIR (con thumbnail)
const {files, dir} = await fetchImagesList();
buildThumbPicker("picker-model", files, onSelectModel);
buildThumbPicker("picker-scene", files, onSelectScene);
const {files, dir} = await refreshPickers();
if (files.length === 0) {
setStatus(`Nessuna immagine in ${dir} (configura IMAGES_DIR in .env)`);
setStatus(`Nessuna immagine in ${dir} (carica file o configura IMAGES_DIR)`);
} else {
setStatus(`${files.length} immagini disponibili in ${dir}`);
setStatus(`${files.length} immagini in ${dir}`);
}
// Upload file nella folder
const upEl = document.getElementById("file-upload");
upEl.addEventListener("change", async (e) => {
const f = e.target.files[0];
if (!f) return;
setStatus(`Caricamento ${f.name} nella cartella...`);
try {
const res = await uploadToFolder(f);
await refreshPickers();
setStatus(`Salvato come ${res.saved_as} (${res.files.length} file totali)`);
} catch (err) {
setStatus(`Errore upload: ${err.message}`);
}
e.target.value = ""; // consente re-upload stesso file
});
document.getElementById("btn-match").addEventListener("click", doMatch);
const slider = document.getElementById("p-min-score");
slider.addEventListener("input", (e) => {
+4
View File
@@ -26,6 +26,10 @@
<div class="picker-list"></div>
</div>
<button class="btn btn-go" id="btn-match">▶ MATCH</button>
<label class="btn" title="Carica nuovo file nella cartella immagini">
⬆ Carica file
<input type="file" id="file-upload" accept="image/*" hidden>
</label>
<span id="status">Seleziona modello, disegna ROI, seleziona scena</span>
</div>
</header>