Vấn đề thường gặp
Thuộc tính accept và capture trên <input type="file"> đôi khi không hoạt động như mong đợi do hiểu lầm về vai trò của chúng.
Thuộc tính accept
Chỉ định loại file được chấp nhận (MIME type hoặc extension). Chỉ mang tính **gợi ý UI** (lọc file trong dialog), **không validate** phía server.
<!-- Chỉ chấp nhận ảnh --> <input type="file" accept="image/*"> <!-- Chỉ chấp nhận video --> <input type="file" accept="video/*"> <!-- Chỉ chấp nhận audio --> <input type="file" accept="audio/*"> <!-- Chỉ định extension cụ thể --> <input type="file" accept=".pdf,.doc,.docx"> <!-- Kết hợp MIME type và extension --> <input type="file" accept="image/png, image/jpeg, .pdf">
Thuộc tính capture
Không thêm tùy chọn "camera" vào dialog chọn file. Nó chỉ **gợi ý ưu tiên** nguồn thu thập media:
capture="user": Ưu tiên camera trước (selfie)capture="environment": Ưu tiên camera sau (chính)capture(boolean): Ưu tiên camera/mic mặc định
Trên mobile, khi có capture + accept="image/*", trình duyệt có thể **mở camera trực tiếp** thay vì hiển thị dialog chọn file.
Code đúng chuẩn
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<title>File Input Capture & Accept</title>
<style>
.demo { max-width: 400px; margin: 2rem auto; padding: 1.5rem; border: 1px solid #ddd; border-radius: 8px; }
.form-group { margin-bottom: 1.5rem; }
label { display: block; margin-bottom: 0.5rem; font-weight: 500; }
input[type="file"] { width: 100%; padding: 0.5rem; border: 1px solid #ccc; border-radius: 4px; }
.note { font-size: 0.875rem; color: #666; margin-top: 0.25rem; }
</style>
</head>
<body>
<div class="demo">
<h2>Demo File Input</h2>
<div class="form-group">
<label>Chụp ảnh từ camera sau (môi trường):</label>
<input type="file" accept="image/*" capture="environment">
<p class="note">Trên mobile: mở camera sau trực tiếp. Desktop: dialog chọn file bình thường.</p>
</div>
<div class="form-group">
<label>Chụp ảnh từ camera trước (selfie):</label>
<input type="file" accept="image/*" capture="user">
<p class="note">Ưu tiên camera trước.</p>
</div>
<div class="form-group">
<label>Chỉ chọn ảnh từ thư viện (không camera):</label>
<input type="file" accept="image/*">
<p class="note">Không có capture attribute → hiển thị dialog chọn file/ảnh.</p>
</div>
<div class="form-group">
<label>Quay video từ camera sau:</label>
<input type="file" accept="video/*" capture="environment">
</div>
<div class="form-group">
<label>Ghi âm từ micro:</label>
<input type="file" accept="audio/*" capture>
</div>
</div>
</body>
</html>
Hành vi trên các nền tảng
| Nền tảng | accept="image/*" capture="environment" | accept="image/*" (không capture) |
|---|---|---|
| iOS Safari | Mở camera sau trực tiếp | Hiện menu: "Chụp ảnh", "Thư viện ảnh", "Chọn file" |
| Android Chrome | Mở camera sau trực tiếp | Hiện menu: "Camera", "Tài liệu", "Hình ảnh" |
| Desktop Chrome/Firefox/Edge | Dialog chọn file bình thường (camera là một tùy chọn) | Dialog chọn file bình thường |
Lỗi thường gặp & Khắc phục
1. accept không lọc được file
Nguyên nhân: `accept` chỉ gợi ý UI, user vẫn có thể chọn "All Files" trong dialog.
Giải pháp: Validate lại phía client (JS) và **bắt buộc validate phía server**.
input.addEventListener('change', (e) => {
const file = e.target.files[0];
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (file && !allowedTypes.includes(file.type)) {
alert('Chỉ chấp nhận JPG, PNG, WebP');
e.target.value = ''; // Reset input
}
});
2. capture không mở camera
Nguyên nhân:
- Trình duyệt không hỗ trợ (desktop thường không mở camera trực tiếp)
- Quyền camera bị từ chối
- Không có camera phù hợp (user/environment)
- Context không secure (HTTP thay vì HTTPS/localhost)
3. Muốn hiển thị preview sau khi chọn
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file && file.type.startsWith('image/')) {
const img = document.createElement('img');
img.src = URL.createObjectURL(file);
img.onload = () => URL.revokeObjectURL(img.src);
document.getElementById('preview').appendChild(img);
}
});
Tóm tắt: Code "đúng" theo câu hỏi gốc
<!-- Chỉ chọn ảnh, không bắt camera --> <input type="file" accept="image/*"> <!-- Ưu tiên camera sau cho ảnh --> <input type="file" accept="image/*" capture="environment"> <!-- Ưu tiên camera trước cho ảnh --> <input type="file" accept="image/*" capture="user">