Upload ảnh đại diện người dùng (Avatar Upload + Preview) là một tính năng phổ biến trong mọi ứng dụng có tài khoản người dùng.
Bạn sẽ học cách:
- Tạo form upload ảnh đại diện
- Hiển thị ảnh preview trước khi gửi
- Gửi ảnh lên server
- Cập nhật giao diện với ảnh mới
Mục tiêu
- Cho phép người dùng chọn ảnh từ máy
- Hiển thị ảnh xem trước
- Gửi ảnh lên backend (dùng FormData)
- Cập nhật ảnh đại diện trên profile
Phần 1: Giao diện Upload trong Vue
Trong AccountView.vue
hoặc một component riêng:
<template>
<div>
<h2>Thông tin cá nhân</h2>
<div class="avatar-upload">
<img :src="preview || currentAvatar" alt="Avatar" class="avatar" />
<input type="file" @change="handleFile" accept="image/*" />
</div>
<button @click="upload" :disabled="!file">Cập nhật ảnh</button>
<p v-if="message">{{ message }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const currentAvatar = ref('https://via.placeholder.com/100')
const file = ref(null)
const preview = ref(null)
const message = ref('')
function handleFile(event) {
const selected = event.target.files[0]
if (!selected) return
file.value = selected
preview.value = URL.createObjectURL(selected)
}
async function upload() {
const formData = new FormData()
formData.append('avatar', file.value)
const res = await fetch('http://localhost:5000/upload-avatar', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + localStorage.getItem('token')
},
body: formData
})
const data = await res.json()
message.value = data.message || 'Đã cập nhật'
if (res.ok && data.url) {
currentAvatar.value = data.url
preview.value = null
file.value = null
}
}
</script>
<style scoped>
.avatar-upload {
display: flex;
align-items: center;
gap: 16px;
}
.avatar {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
border: 2px solid #ddd;
}
</style>
Phần 2: Backend xử lý upload ảnh
Cài middleware multer
:
npm install multer
Tạo route upload trong index.js
:
const multer = require('multer')
const path = require('path')
const storage = multer.diskStorage({
destination: 'uploads/',
filename: (req, file, cb) => {
const ext = path.extname(file.originalname)
cb(null, Date.now() + ext)
}
})
const upload = multer({ storage })
app.use('/uploads', express.static('uploads'))
app.post('/upload-avatar', authMiddleware, upload.single('avatar'), async (req, res) => {
if (!req.file) return res.status(400).json({ error: 'Chưa chọn ảnh' })
const imageUrl = `http://localhost:5000/uploads/${req.file.filename}`
const user = await User.findById(req.userId)
user.avatar = imageUrl
await user.save()
res.json({ message: 'Đã cập nhật ảnh đại diện', url: imageUrl })
})
Trong User.js
, thêm field:
avatar: { type: String, default: '' }
Phần 3: Hiển thị avatar trong toàn app
Ví dụ trong header hoặc dashboard:
<img :src="auth.user.avatar || defaultAvatar" class="avatar-small" />
Bạn đã học được
- Cách chọn ảnh và preview bằng
URL.createObjectURL
- Gửi ảnh qua
FormData
bằng fetch API - Xử lý upload ảnh bằng multer
- Cập nhật ảnh đại diện cho người dùng
Bài tập mở rộng
- Giới hạn kích thước file (< 1MB), kiểm tra định dạng
- Dùng Cloudinary để lưu ảnh cloud, không cần lưu server
- Cho phép cắt ảnh (crop) trước khi upload
- Hiển thị ảnh mặc định nếu chưa có avatar
Thảo luận