Auto-suffix duplicate filenames in web bulk upload
The server derives a photo's slug from the uploaded filename, so two photos with the same name in one batch would overwrite each other. Each card now gets a slug that is unique among the current cards (second same-named photo becomes -2, -3, …), computed with a client mirror of the server's sanitiseSlug and sent as the multipart filename. The slug is assigned once when a photo is added, so it stays stable across retries. Kept client-side on purpose: a server-side "slug exists -> suffix" would turn the apps' idempotent re-upload retries (same filename overwrites the same post) into duplicates.
This commit is contained in:
1 parent
c21ddc5b7e
commit
003a4057bf
1 file changed
+29
-2
+29
-2
@@ -94,8 +94,33 @@
|
|||||||
refresh();
|
refresh();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Mirror of the server's sanitiseSlug, so the client can predict the slug a
|
||||||
|
// filename yields and keep two files in one batch from colliding.
|
||||||
|
function slugify(name) {
|
||||||
|
let out = '';
|
||||||
|
for (const ch of name.toLowerCase()) {
|
||||||
|
if ((ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch === '-') out += ch;
|
||||||
|
else if (ch === ' ' || ch === '_') out += '-';
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A slug unique among the current cards. The server derives the slug from
|
||||||
|
// the uploaded filename, so two photos named the same would otherwise
|
||||||
|
// overwrite each other; the second (third, …) gets -2, -3, …
|
||||||
|
function uniqueSlug(filename) {
|
||||||
|
const stem = filename.replace(/\.[^.]+$/, '');
|
||||||
|
const base = slugify(stem) || 'photo';
|
||||||
|
let candidate = base;
|
||||||
|
let n = 2;
|
||||||
|
while (items.some((i) => i.slug === candidate)) candidate = base + '-' + (n++);
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
|
||||||
function addItem(file) {
|
function addItem(file) {
|
||||||
const item = { id: ++seq, file, status: 'pending', el: null };
|
const ext = (file.name.match(/\.[^.]+$/)?.[0] || '.jpg').toLowerCase();
|
||||||
|
const item = { id: ++seq, file, ext, slug: '', status: 'pending', el: null };
|
||||||
|
item.slug = uniqueSlug(file.name); // before push, so it compares against the others
|
||||||
items.push(item);
|
items.push(item);
|
||||||
item.el = buildCard(item);
|
item.el = buildCard(item);
|
||||||
list.appendChild(item.el);
|
list.appendChild(item.el);
|
||||||
@@ -177,7 +202,9 @@
|
|||||||
setStatus(item, 'up', 'Uploading…');
|
setStatus(item, 'up', 'Uploading…');
|
||||||
|
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
form.append('image', item.file);
|
// Send under the deduped slug so the server (which slugs from the
|
||||||
|
// filename) can't overwrite another photo in the same batch.
|
||||||
|
form.append('image', item.file, item.slug + item.ext);
|
||||||
form.append('caption', item.el.querySelector('.c-caption').value);
|
form.append('caption', item.el.querySelector('.c-caption').value);
|
||||||
form.append('date', item.el.querySelector('.c-date').value);
|
form.append('date', item.el.querySelector('.c-date').value);
|
||||||
form.append('tags', item.el.querySelector('.c-tags').value);
|
form.append('tags', item.el.querySelector('.c-tags').value);
|
||||||
|
|||||||
Reference in new issue
Block a user