Списък с файлове с правилната икона до всяко име се чете с един поглед: PDF файлът е червен, таблицата зелена, архивът кафяв. Списък, в който половината икони са празен лист, се чете ред по ред. Празните листове идват от няколко познати случая: REPORT.PDF с главни букви, backup.tar.gz, показан като обикновен gzip файл, снимка от телефон с име IMG_0042 без никакво разширение, CSV файл, който браузърът нарича файл на Excel, и MIME тип с ; charset=utf-8 в края. Тук показваме откъде всъщност идва типът на файла, ред от правила, който дава правилната икона във всички тези случаи, и страната на сървъра, където иконата е най-малката грижа. JavaScript е пуснат на Node 22, а PHP на PHP 8.4, точно както са отпечатани.
#Откъде идва типът
Източниците са три и нито един от тях сам по себе си не е истината.
- Името. Разширението е избрано от този, който е кръстил файла. Обикновено е вярно и е единственото, с което разполагате за файл в списък, който не сте отворили.
- MIME типът от браузъра.
File.typeв браузъра не се чете от файла. Справочникът на MDN казва, че той се предполага по разширението, че необичайните разширения дават празен низ и че настройките на системата, например регистърът на Windows, могат да дадат неочаквани стойности дори за обичайни типове. Затова файл.csvот компютър с Windows и инсталиран Excel често пристига катоapplication/vnd.ms-excel. При изпращане на формата същата стойност отива на сървъра катоContent-Typeна частта. - Байтовете. Само сървърът може да погледне във файла, с библиотека като
finfoв PHP, която разпознава форматите по първите им байтове.
#Ред от правила, който работи
За иконата най-добрият източник е името, а MIME типът е резервата. Проверявайте ги в този ред: име, завършващо с наклонена черта, е папка; после двойните разширения като .tar.gz; после последното разширение с малки букви, което покрива и файлове с точка отпред като .env; после точния MIME тип без параметрите му; после семейството на MIME типа, така че всяко image/... получава обща икона за изображение; и едва накрая общ файл.
const EXT = { pdf: "pdf", docx: "docx", xlsx: "xlsx", csv: "csv", png: "png", jpg: "jpg", jpeg: "jpg",
heic: "heic", mp4: "mp4", zip: "zip", tgz: "tgz", json: "json", env: "env" };
const DOUBLE = { "tar.gz": "tgz", "tar.bz2": "bz2", "tar.xz": "xz" };
const MIME = { "application/pdf": "pdf", "image/heic": "heic", "text/csv": "csv",
"application/vnd.ms-excel": "xls", "application/zip": "zip" };
const FAMILY = ["image", "audio", "video", "text", "font", "model"];
export function iconName(fileName, mime = "") {
const name = String(fileName);
if (name.endsWith("/")) return "folder";
const base = name.split(/[\\/]/).pop().toLowerCase();
for (const [double, icon] of Object.entries(DOUBLE)) {
if (base.endsWith("." + double)) return icon;
}
const dot = base.lastIndexOf(".");
if (dot >= 0 && Object.hasOwn(EXT, base.slice(dot + 1))) return EXT[base.slice(dot + 1)];
const type = mime.split(";")[0].trim().toLowerCase();
if (Object.hasOwn(MIME, type)) return MIME[type];
const family = type.split("/")[0];
return FAMILY.includes(family) ? `kind-${family}` : "kind-file";
}
Таблиците тук са съкратени за примера; истински набор покрива доста над сто разширения. Пуснахме го върху десет имена:
Annual report 2026.PDFбез тип даваpdf: разширението с главни букви пак се брои.backup.tar.gzдаваtgz, а не иконата за gzip, защото двойните разширения се проверяват първи.IMG_0042сimage/heicдаваheic: няма разширение, затова решава точният MIME тип.prices.csvсapplication/vnd.ms-excelдаваcsv: името печели пред догадката на браузъра.scan.weirdсimage/x-portable-anymapдаваkind-image, а голотоnotesбез тип даваkind-file.photos/е папка,.envдаваenv, път от Windows, завършващ наОтчет.docx, даваdocx, аdataсtext/csv; charset=utf-8даваcsv.
Един малък детайл е важен: търсенето ползва Object.hasOwn(). С обикновено ext in EXT файл с име x.constructor би намерил собствената функция constructor на обекта и би я върнал като име на икона. С hasOwn той получава общата икона.
#На сървъра иконата е етикет, а не проверка
След качване можете да направите повече от името, защото имате байтовете. Прочетете типа от съдържанието, покажете го до името и дръжте двете решения отделно: иконата следва името, защото него вижда и разпознава човекът; дали файлът се приема, пази и сервира, следва разпознатия тип и другите ви проверки.
<?php
// The type a browser sends with an upload comes from the client; read the file itself instead.
function detected_type(string $path): string
{
return (new finfo(FILEINFO_MIME_TYPE))->file($path) ?: 'application/octet-stream';
}
function icon_row(string $originalName, string $storedPath, string $iconBase = '/icons/'): string
{
$type = detected_type($storedPath);
$icon = icon_name($originalName, $type); // the same rules as the JavaScript above
$name = htmlspecialchars($originalName, ENT_QUOTES);
return sprintf('<li><img src="%s%s.svg" width="24" height="24" alt=""> %s <small>%s</small></li>',
$iconBase, $icon, $name, htmlspecialchars($type, ENT_QUOTES));
}
icon_name() е PHP версия на функцията на JavaScript по-горе. Пуснахме icon_row() върху малък PNG, качен два пъти. С име holiday "photo", без разширение, той получава иконата за изображение от разпознатия тип image/png, а кавичките в името излизат екранирани. С име <script>.pdf получава иконата за PDF от името си, докато дребният текст казва image/png, а името се показва като текст, а не се изпълнява като таг. Икона на PDF върху PNG е безобидна в списък; да оставите името да реши какво е файлът, не би било. Наръчникът на OWASP за качване на файлове отива по-далеч: позволявайте само списък от разширения, не вярвайте на заглавката Content-Type, защото лесно се подправя, проверявайте и сигнатурата на файла, но не само нея, и пазете файловете под имена, които приложението ви генерира.
#Как да показвате иконата
- Когато името на файла е написано до иконата, иконата е декоративна: дайте ѝ празен
alt="", както препоръчва ръководството на W3C за изображения, за да не четат екранните четци „PDF, Annual report 2026.PDF“. - Задайте
widthиheightна изображението, за да не подскача списъкът, докато иконите се зареждат. - За дълъг списък един SVG спрайт с
<use href="#pdf">е една заявка вместо по една за всяка икона, а контурен стил, рисуван вcurrentColor, следва цвета на текста, и в тъмен режим. - Ако иконата показва разширението като текст, този текст има нужда от контраст като всеки друг: 4,5:1 за нормален текст по WCAG 2.2.
#Набор от икони с помощниците
Направихме Faylche, набор от 202 икони за типове файлове: по една за 172 разширения, по една за 28 вида файлове и две папки, в четири стила (бял лист с цветен етикет, плътен цвят, контур в currentColor и малък стил, рисуван за 16 и 24 пиксела), като SVG, готови PNG файлове и спрайтове. Помощниците му на JavaScript и PHP следват точно реда от правила по-горе, със 77 точни MIME типа, а тестовете проверяват, че JavaScript, PHP и изграждането на Python дават един и същ отговор за едни и същи имена. Бялото върху всеки цвят и всеки цвят върху листа имат контраст поне 4,5:1. Помощниците са пуснати с Node.js 22 и PHP 8.4 в тестовете; програми за дизайн като Figma не са отваряни. Наборът е обявен като „Очаквайте скоро“.
#Често задавани въпроси
Защо браузърът казва, че моят CSV файл е файл на Excel?
Защото File.type идва от настройките на системата за разширението, а не от файла. В Windows с инсталиран Excel .csv често е регистриран като application/vnd.ms-excel. Решавайте иконата първо по разширението.
Мога ли да вярвам на MIME типа на качен файл?
Не. Типът, изпратен с качването, идва от клиента. Прочетете типа от съдържанието на файла на сървъра, преди да го приемете, запазите или сервирате.
Каква икона да получи файл с непознато разширение?
Ползвайте семейството на MIME типа му, когато го имате, например обща икона за изображение или звук, и обща икона за файл, когато нямате нищо.
Трябва ли иконата на файл да има алтернативен текст?
Не и когато името на файла е написано до нея: тогава иконата е декоративна и alt="" е правилно. Икона, която стои сама, има нужда от текст, който казва какъв е файлът.
Коментари
Коментари
Напишете първия коментар.
Оставете коментар