Đăng nhập

📘 BÀI 28: UPLOAD ẢNH ĐẠI DIỆN NGƯỜI DÙNG (AVATAR)

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

  1. Giới hạn kích thước file (< 1MB), kiểm tra định dạng
  2. Dùng Cloudinary để lưu ảnh cloud, không cần lưu server
  3. Cho phép cắt ảnh (crop) trước khi upload
  4. Hiển thị ảnh mặc định nếu chưa có avatar

Thảo luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *

Đăng ký nhận tin mới

Nhận bài học, tài nguyên và cơ hội việc làm qua email hàng tuần.

Chúng tôi cam kết không spam. Bạn có thể hủy bất cứ lúc nào.