前:vue 后:django 部署:supervisor+nginx 流程及部分问题简记
文章目录
- 前:vue 后:django 部署:supervisor+nginx 流程及部分问题简记
- 一、服务器环境准备
- 二、后端 django部署
- 三、nginx处配置
- ps:补充一个问题:如有涉及文件上传,nginx处还需配置,之前在本地测无问题,部署后上传失败,简记流程
前:vue 后:django 部署:supervisor+nginx 流程及部分问题简记
一、服务器环境准备
安装必要的软件:
sudo apt update && sudo apt install -y python3-pip python3-venv supervisor
如果是 CentOS:
sudo yum install -y python3-pip python3-venv supervisor
检查是否安装成功:
python3 --version
pip3 --version
supervisord --version
二、后端 django部署
克隆项目至服务器本地:
cd /data
git clone https://github.com/your-repo/django-project.git
cd django-project
创建 Python 虚拟环境:
python3 -m venv venv
source venv/bin/activate
# 安装依赖
pip install --upgrade pip
pip install -r requirements.txt
安装gunicorn:
pip install gunicorn
# 测试运行 Gunicorn
gunicorn --workers 3 --bind 0.0.0.0:8000 django-project.wsgi:application
配置Supervisor:
在项目文件夹下创建 supervisord.d 文件夹,下创建django-project-wsgi.ini
[program:django-project-wsgi]
directory = /data/gitcode/django-project
command = /data/gitcode/django-project/venv/bin/gunicorn -w4 -b 0.0.0.0:8000 django-project.wsgi --timeout 1800 --keep-alive 30
numprocs = 1
autostart = true
autorestart = true
redirect_stderr = true
stdout_logfile = /data/gitcode/logs/django-project.log
stderr_logfile = /data/gitcode/logs/django-project_err.log
可以在该项目文件夹下创建启动的shell脚本:
#!/bin/bash
sudo supervisorctl restart django-project-wsgi
三、nginx处配置
server {
listen 8082;
server_name 【服务器IP】;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html/dist; # 前端vue打包的dist放置位置
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://【服务器IP】:8000;
}
location @router {
rewrite ^.*$ /index.html last;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
可写一个shell脚本重启:
sudo systemctl restart nginx
sudo /usr/local/nginx/sbin/nginx -s reload
sudo chmod -R 777 /usr/local/nginx/html/dist
ps:补充一个问题:如有涉及文件上传,nginx处还需配置,之前在本地测无问题,部署后上传失败,简记流程
vue:
<template>
<Card style="width: 100%; height: 115%">
<div>
<Upload
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
accept=".csv"
>
<Button type="primary">上传 CSV 文件</Button>
</Upload>
<div style="height: 10px"></div>
<Alert show-icon>
已上传文件: <span>{{ uploadedFileName }}</span>
</Alert>
<div v-if="duplicates.length > 0">
<Alert show-icon type="warning">
重复的 MAC 地址 <b>({{ duplicates.length }})</b>:
<Scroll :height="400">
<ul>
<li v-for="mac in duplicates" :key="mac">{{ mac }}</li>
</ul>
</Scroll>
</Alert>
<Button type="success" @click="downloadDuplicates">下载重复的 MAC 地址</Button>
</div>
<div v-else>
<Alert show-icon type="success">
没有重复的 MAC 地址
</Alert>
</div>
</div>
</Card>
</template>
<script>
import { Upload, Button, Alert, Card, Scroll } from 'iview';
import axios from 'axios';
export default {
components: {
Upload,
Button,
Alert,
Card,
Scroll
},
data() {
return {
uploadedFileName: '',
duplicates: []
};
},
methods: {
beforeUpload(file) {
this.uploadedFileName = file.name;
const formData = new FormData();
formData.append('file', file);
axios.post('/api/upload-csv/', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
.then(response => {
this.handleSuccess(response);
})
.catch(error => {
this.handleError(error);
});
return false; // Prevent default upload behavior
},
handleSuccess(response) {
this.$Message.success('文件上传成功');
this.duplicates = response.data.duplicates;
},
handleError(error) {
this.$Message.error('文件上传失败');
},
downloadDuplicates() {
const blob = new Blob([this.duplicates.join(', ')], { type: 'text/plain' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'duplicates.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
}
};
</script>
<style scoped>
.custom-bread-crumb {
margin-bottom: 20px;
}
</style>
export const uploadCsvFile = (file, token) => {
const formData = new FormData();
formData.append('file', file);
return axios.request({
url: '/api/upload-csv/',
method: 'post',
headers: {
'Authorization': `Token ${token}`,
'Content-Type': 'multipart/form-data'
},
data: formData
})
}
django views.py
UPLOAD_FOLDER = 'uploads'
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER)
class HandleNacMultipulRecords(APIView):
def post(self, request):
if 'file' not in request.FILES:
return Response({'error': 'No file part'}, status=status.HTTP_400_BAD_REQUEST)
file = request.FILES['file']
if file.name == '':
return Response({'error': 'No selected file'}, status=status.HTTP_400_BAD_REQUEST)
if file.name.endswith('.csv'):
file_path = os.path.join(UPLOAD_FOLDER, file.name)
with open(file_path, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
# 解析 CSV 文件并提取 MAC Address 列
mac_addresses = []
with open(file_path, newline='', encoding='utf-8') as csvfile:
csv_reader = csv.DictReader(csvfile)
for row in csv_reader:
mac_addresses.append(row['MAC Address'])
# 删除上传的文件
# os.remove(file_path)
# 验证 MAC 地址格式
valid_mac_addresses = [mac for mac in mac_addresses if
re.match(r'^([0-9A-Fa-f]{2}){5}[0-9A-Fa-f]{2}$', mac)]
mac_counter = Counter(valid_mac_addresses)
duplicates = [mac for mac, count in mac_counter.items() if count > 1]
logger.info(duplicates)
return Response({
'message': 'File uploaded successfully',
'valid_mac_addresses': valid_mac_addresses,
'duplicates': duplicates
}, status=status.HTTP_200_OK)
return Response({'error': 'Invalid file type'}, status=status.HTTP_400_BAD_REQUEST)
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
client_max_body_size 100M; # 注意:这里需设置,否则会上传失败!
#gzip on;
include /usr/local/nginx/conf/conf.d/*.conf;
...