项目结构

avatar-uploader/
├── public/
│   ├── index.html          # 前端页面
│   ├── style.css           # 样式文件
│   └── script.js           # 前端逻辑
├── uploads/                # 存储上传的头像(需手动创建)
├── server.js               # Node.js后端服务
└── package.json            # 项目配置

前端代码 (public/index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">AE头像上传</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h2>上传AE头像</h2>
        <div class="avatar-preview">
            <img id="avatar-preview" src="default-avatar.png" alt="头像预览">
        </div>
        <input type="file" id="avatar-input" accept="image/*">
        <button id="upload-btn">上传头像</button>
        <div id="message"></div>
    </div>
    <script src="script.js"></script>
</body>
</html>

样式文件 (public/style.css)

body {
    font-family: Arial, sans-serif;
    display: flex;
    justify-content: center;
    align-items: center;
    min-height: 100vh;
    background: #f5f5f5;
}
.container {
    text-align: center;
    background: white;
    padding: 30px;
    border-radius: 10px;
    box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.avatar-preview {
    width: 150px;
    height: 150px;
    margin: 0 auto 20px;
    border-radius: 50%;
    overflow: hidden;
    border: 3px solid #ddd;
}
.avatar-preview img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
#avatar-input {
    display: none;
}
#upload-btn {
    padding: 10px 20px;
    background: #4CAF50;
    color: white;
    border: none;
    border-radius: 5px;
    cursor: pointer;
    margin-top: 10px;
}
#message {
    margin-top: 15px;
    color: #666;
}

前端逻辑 (public/script.js)

document.getElementById('upload-btn').addEventListener('click', () => {
    document.getElementById('avatar-input').click();
});
document.getElementById('avatar-input').addEventListener('change', (e) => {
    const file = e.target.files[0];
    if (file) {
        const reader = new FileReader();
        reader.onload = (e) => {
            document.getElementById('avatar-preview').src = e.target.result;
        };
        reader.readAsDataURL(file);
    }
});
document.getElementById('upload-btn').addEventListener('click', async () => {
    const fileInput = document.getElementById('avatar-input');
    const file = fileInput.files[0];
    if (!file) {
        document.getElementById('message').textContent = '请选择头像文件!';
        return;
    }
    const formData = new FormData();
    formData.append('avatar', file);
    try {
        const response = await fetch('/upload', {
            method: 'POST',
            body: formData
        });
        const result = await response.json();
        if (result.success) {
            document.getElementById('message').textContent = `头像上传成功!路径: ${result.path}`;
        } else {
            document.getElementById('message').textContent = `上传失败: ${result.error}`;
        }
    } catch (error) {
        document.getElementById('message').textContent = '服务器错误!';
    }
});

后端代码 (server.js)

const express = require('express');
const multer = require('multer');
const path = require('path');
const fs = require('fs');
const app = express();
const port = 3000;
// 确保uploads目录存在
const uploadDir = 'uploads';
if (!fs.existsSync(uploadDir)) {
    fs.mkdirSync(uploadDir);
}
// 配置文件存储
const storage = multer.diskStorage({
    destination: (req, file, cb) => {
        cb(null, 'uploads/');
    },
    filename: (req, file, cb) => {
        // 生成唯一文件名
        const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9);
        cb(null, uniqueSuffix + path.extname(file.originalname));
    }
});
const upload = multer({ 
    storage: storage,
    limits: { fileSize: 2 * 1024 * 1024 }, // 2MB限制
    fileFilter: (req, file, cb) => {
        // 只允许图片文件
        const allowedTypes = /jpeg|jpg|png|gif/;
        const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase());
        const mimetype = allowedTypes.test(file.mimetype);
        if (mimetype && extname) {
            return cb(null, true);
        } else {
            cb(new Error('只允许上传图片文件!'));
        }
    }
});
// 静态文件服务
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));
// 头像上传接口
app.post('/upload', upload.single('avatar'), (req, res) => {
    if (!req.file) {
        return res.status(400).json({ 
            success: false, 
            error: '未选择文件或文件类型错误!' 
        });
    }
    const filePath = `/uploads/${req.file.filename}`;
    res.json({ 
        success: true, 
        path: filePath 
    });
});
// 错误处理
app.use((err, req, res, next) => {
    if (err instanceof multer.MulterError) {
        res.status(400).json({ success: false, error: err.message });
    } else if (err) {
        res.status(500).json({ success: false, error: err.message });
    }
});
app.listen(port, () => {
    console.log(`服务器运行在 http://localhost:${port}`);
});

项目配置 (package.json)

{
  "name": "ae-avatar-uploader",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "dependencies": {
    "express": "^4.18.2",
    "multer": "^1.4.5-lts.1"
  }
}

使用步骤

  1. 安装依赖

    AE头像源码打包下载在哪里?包含哪些资源?

    npm install
  2. 创建目录

    mkdir uploads
  3. 启动服务

    AE头像源码打包下载在哪里?包含哪些资源?

    npm start
  4. 访问页面: 打开浏览器访问 http://localhost:3000


功能说明

  • ✅ 本地预览上传的头像
  • ✅ 文件类型校验(仅允许图片)
  • ✅ 文件大小限制(2MB)
  • ✅ 自动生成唯一文件名
  • ✅ 上传成功后返回文件路径

安全注意事项

  1. 生产环境加固

    • 添加用户认证(如JWT)
    • 使用云存储(如AWS S3)替代本地存储
    • 限制上传频率(防滥用)
    • 添加CSRF防护
  2. 部署建议

    • 使用Nginx反向代理
    • 配置HTTPS
    • 定期清理uploads目录

源码打包下载点击下载完整项目源码(示例链接,需替换为实际仓库地址)

AE头像源码打包下载在哪里?包含哪些资源?

相关内容

回顶部