ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Django+Mysql个人网盘源码实战:从环境搭建到权限安全

Django+Mysql个人网盘源码实战:从环境搭建到权限安全 简介这是一套基于DjangoMysql开发的个人网盘与云盘系统源程序面向具备Python基础、想学习Web全栈开发或搭建私有存储服务的开发者。项目采用Django框架组织后端逻辑配合Mysql完成数据持久化涵盖用户认证、文件管理、权限控制等网盘核心模块适合作为课程设计、毕业设计或二次开发的基础工程。压缩包共66个文件约1.21MB其中21个py文件承载视图、模型、序列化与配置等后端逻辑15个html与10个js构成前端页面与交互另有4个sql文件提供角色、权限、文件类型等基础数据辅以图片、样式及说明文档结构完整。资源附有依赖安装、配置修改、数据库迁移、基础SQL导入、超级用户创建与本地启动的完整流程说明便于快速跑通项目。目前已有1495人学习下载读者可借此理解Django项目分层设计、REST接口组织与网盘业务建模思路并在此基础上扩展分享、回收站等功能。1. 从一份 Django 网盘源码说起个人云盘系统到底该怎么落地很多人第一次接触「Python个人网盘源码、云盘系统源程序基于DjangoMysql」这个标题脑子里冒出来的第一个念头是下载下来、装个 Python、跑起来是不是就能拥有一个自己的百度网盘了我当年也是这么想的结果第一次跑别人的 Django 项目光mysqlclient编译就卡了一下午。个人网盘系统本质上是一个「文件上传 存储 权限 分享」的 Web 应用Django 负责请求路由、用户体系和后台管理Mysql 负责存用户、文件元数据和分享记录真正的文件本体一般落在本地磁盘或对象存储上。它适合两类人一类是想拿一个完整项目练 Django 全栈的新手另一类是想给自己或小团队搭一个私有文件管理入口的运维、后端。这篇文章不讲空话就按「源码结构怎么读 → 环境怎么搭 → 上传下载怎么实现 → 权限和分享怎么做 → 坑在哪」这条线把一份 Django 云盘源码从能跑到能用讲透。2. 读懂 Django 云盘源码的目录结构与数据模型拿到一份 Django 网盘源码别急着runserver先花二十分钟把目录和数据模型看明白后面能省掉大量「改了没反应」的玄学问题。Django 项目是强约定框架目录结构基本固定看懂settings.py和models.py这两个文件整个系统的骨架就清楚了。2.1 一个典型 Django 网盘项目的目录长什么样常见的目录结构大致是这样不同作者会有出入但核心文件跑不掉netdisk/ ├── manage.py ├── netdisk/ # 项目配置包 │ ├── settings.py # 数据库、静态文件、中间件配置 │ ├── urls.py # 根路由 │ └── wsgi.py ├── users/ # 用户 App注册、登录、个人中心 │ ├── models.py │ ├── views.py │ └── urls.py ├── files/ # 文件 App上传、下载、删除、分享 │ ├── models.py │ ├── views.py │ └── urls.py ├── static/ # CSS、JS、图片 ├── media/ # 用户上传的文件实际落盘位置 └── templates/ # HTML 模板users和files两个 App 是核心。users管身份files管内容。media目录是文件本体存放的地方static是前端资源这两个目录在settings.py里必须配好否则会出现「上传成功但下载 404」或者「CSS 加载不出来」的经典问题。我一般会先grep -r MEDIA_ROOT\|STATIC_ROOT netdisk/settings.py确认路径再决定要不要改。2.2 数据模型用户、文件、分享三张表怎么设计Django 的 ORM 把表结构写在models.py里网盘系统的核心模型通常就三个。下面是我常用的一个精简版本可以直接对照源码看# files/models.py from django.db import models from django.contrib.auth.models import User class UserFile(models.Model): owner models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name所属用户) file_name models.CharField(max_length255, verbose_name文件名) file_path models.CharField(max_length500, verbose_name存储路径) file_size models.BigIntegerField(default0, verbose_name文件大小(字节)) upload_time models.DateTimeField(auto_now_addTrue, verbose_name上传时间) is_deleted models.BooleanField(defaultFalse, verbose_name软删除标记) class Meta: db_table user_file indexes [models.Index(fields[owner, is_deleted])] class ShareLink(models.Model): file models.ForeignKey(UserFile, on_deletemodels.CASCADE) token models.CharField(max_length64, uniqueTrue, verbose_name分享令牌) expire_at models.DateTimeField(verbose_name过期时间) extract_code models.CharField(max_length8, blankTrue, verbose_name提取码)这里有几个参数值得说清楚。file_size用BigIntegerField而不是IntegerField因为IntegerField在 Mysql 里对应int最大约 21 亿字节也就是 2GB 左右超过就溢出这是很多人上传大文件失败却查不出原因的根源。is_deleted做软删除用户点删除只是把标记置为True文件本体和记录都保留方便做回收站也避免误删后没有后悔药。ShareLink里的token用uniqueTrue保证分享链接不重复expire_at控制有效期extract_code就是常见的提取码。Meta里的联合索引[owner, is_deleted]很关键网盘列表页几乎每次查询都带这两个条件没有索引在文件多了以后会明显变慢。2.3 从 models 到数据库迁移命令与字段类型对照模型写好后靠 Django 的迁移系统同步到 Mysqlpython manage.py makemigrations files python manage.py migratemakemigrations根据models.py的变化生成迁移脚本migrate把脚本执行到数据库。如果改了字段类型比如把file_size从IntegerField改成BigIntegerFieldDjango 会生成AlterField但已有数据不会自动转换数据量大时要自己写数据迁移。字段类型和 Mysql 的对应关系建议记牢CharField→varcharTextField→longtextDateTimeField→datetimeBooleanField→tinyint(1)。搞混了在navicat里看表结构会一脸懵。3. 把 DjangoMysql 网盘系统在本地跑起来环境搭建是新手翻车最集中的环节尤其是 Mysql 和 Python 驱动的连接。这一章按顺序把 Python、Mysql、依赖、配置四步走完每一步都给可复制的命令和排错方向。3.1 Python 环境与虚拟环境的准备先确认 Python 版本Django 3.x 建议 Python 3.7 以上Django 4.x 建议 3.8 以上python --version # 或 python3 --version然后建虚拟环境别把依赖装到全局否则多个项目版本冲突会让你怀疑人生python -m venv venv # Windows venv\Scripts\activate # Linux / macOS source venv/bin/activate激活后命令行前面会出现(venv)。这一步在vscode里也要注意右下角解释器要选到虚拟环境里的 Python否则vscode python环境配置没配对终端里装的包编辑器识别不到代码里全是黄色波浪线。3.2 Mysql 安装与数据库初始化Windows 用户去 Mysql 官网下载安装包安装时记住 root 密码字符集选utf8mb4。Linux 用户一条命令sudo apt install mysql-server sudo mysql_secure_installation装好后登录建库CREATE DATABASE netdisk DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; CREATE USER netdisklocalhost IDENTIFIED BY 你的密码; GRANT ALL PRIVILEGES ON netdisk.* TO netdisklocalhost; FLUSH PRIVILEGES;字符集一定要用utf8mb4不要用utf8。Mysql 的utf8是残缺的三字节实现存不了 emoji 和部分生僻字用户上传一个带 emoji 的文件名就会报错这是血泪经验。建库时如果报ERROR 2002 (HY000): Cant connect to local MySQL server through socket说明 Mysql 服务没起来Linux 下sudo service mysql start即可。3.3 依赖安装与 settings.py 数据库配置网盘项目常见依赖pip install django mysqlclient pillowmysqlclient是 Django 连 Mysql 的驱动Windows 上如果编译失败可以改用pymysql然后在项目__init__.py里加import pymysql pymysql.install_as_MySQLdb()接着改settings.pyDATABASES { default: { ENGINE: django.db.backends.mysql, NAME: netdisk, USER: netdisk, PASSWORD: 你的密码, HOST: 127.0.0.1, PORT: 3306, OPTIONS: {charset: utf8mb4}, } }HOST写127.0.0.1而不是localhost能避开一部分 socket 连接问题。OPTIONS里的charset和建库时保持一致。如果报mysql ssl连接错误在OPTIONS里加ssl: {ssl_mode: DISABLED}先绕过本地开发不需要 SSL。3.4 迁移、建超级用户与启动python manage.py migrate python manage.py createsuperuser python manage.py runservermigrate会把 Django 内置的 auth、admin 等表和你的业务表一起建好。createsuperuser建后台管理员之后访问/admin能直接管理用户和文件记录。runserver默认跑在127.0.0.1:8000。如果页面能开但样式全丢检查settings.py里STATIC_URL和DEBUG开发阶段DEBUGTrue时 Django 才会自动托管静态文件vscode写img标签在django的static文件中显示不了多半就是DEBUG关了或者路径没写{% static %}。4. 文件上传、下载与分享的核心实现环境跑通只是第一步网盘真正有价值的是文件流转。这一章讲上传落盘、下载响应、分享链接三个核心功能的实现思路和关键参数。4.1 文件上传落盘路径与大小限制上传视图的核心是把request.FILES里的文件写到MEDIA_ROOT下并记录元数据# files/views.py import os, uuid from django.conf import settings from django.shortcuts import render, redirect from .models import UserFile def upload(request): if request.method POST and request.FILES.get(file): f request.FILES[file] # 用 uuid 重命名避免同名覆盖和中文名乱码 ext os.path.splitext(f.name)[1] save_name f{uuid.uuid4().hex}{ext} save_path os.path.join(settings.MEDIA_ROOT, save_name) with open(save_path, wb) as dest: for chunk in f.chunks(): dest.write(chunk) UserFile.objects.create( ownerrequest.user, file_namef.name, file_pathsave_name, file_sizef.size, ) return redirect(file_list) return render(request, upload.html)关键点有三个。第一存储文件名用uuid重命名原始文件名只存数据库这样能避免同名覆盖也能绕开中文文件名在不同系统下的编码问题。第二用f.chunks()分块写入而不是f.read()一次性读大文件不会把内存撑爆。第三Django 默认上传大小限制约 2.5MB超过会报错要在settings.py里放开DATA_UPLOAD_MAX_MEMORY_SIZE 104857600 # 100MB FILE_UPLOAD_MAX_MEMORY_SIZE 104857600注意这两个参数控制的是「内存中处理的大小」超过阈值的部分 Django 会写到临时文件真正限制大文件还要看 Nginx 或前端。如果部署在 Nginx 后面client_max_body_size也要同步改否则请求根本到不了 Django。4.2 文件下载流式响应与权限校验下载不能直接把文件路径返回给前端那样任何人都能猜到地址。正确做法是走视图做权限校验再用FileResponse流式返回from django.http import FileResponse, Http404 import os from django.conf import settings def download(request, file_id): try: uf UserFile.objects.get(idfile_id, ownerrequest.user, is_deletedFalse) except UserFile.DoesNotExist: raise Http404(文件不存在) full_path os.path.join(settings.MEDIA_ROOT, uf.file_path) if not os.path.exists(full_path): raise Http404(文件已丢失) response FileResponse(open(full_path, rb), as_attachmentTrue, filenameuf.file_name) return responseownerrequest.user保证只能下自己的文件is_deletedFalse过滤回收站。as_attachmentTrue让浏览器弹下载框而不是直接打开filename用数据库里的原始名用户下载下来还是原来的名字。FileResponse是流式的大文件不会一次性读进内存。这里有个坑如果文件被手动从media目录删了但数据库记录还在os.path.exists判断能兜住返回 404 而不是 500。4.3 分享链接token 生成与过期控制分享功能的核心是生成一个不可猜测的 token并带上过期时间import uuid from datetime import timedelta from django.utils import timezone from .models import ShareLink def create_share(request, file_id): uf UserFile.objects.get(idfile_id, ownerrequest.user) token uuid.uuid4().hex ShareLink.objects.create( fileuf, tokentoken, expire_attimezone.now() timedelta(days7), extract_coderequest.POST.get(code, ), ) return redirect(share_detail, tokentoken)uuid4().hex生成 32 位随机串碰撞概率极低也不像自增 ID 那样能被遍历。expire_at用timezone.now()加天数注意要用 Django 的timezone而不是 Python 原生datetime否则在USE_TZTrue的项目里会出现时区偏差分享链接提前或延后失效。访问分享页时校验expire_at timezone.now()过期就返回提示。提取码为空表示公开分享非空则要求输入匹配。5. 权限、安全与性能网盘系统绕不开的坑功能能跑不等于能用网盘系统一旦放到公网权限、安全和性能问题会集中爆发。这一章把最常见的几类坑按「现象 → 原因 → 解决」列清楚。5.1 越权访问改一个 URL 就能看别人的文件现象用户 A 登录后把下载链接里的file_id改成 B 的文件 ID居然能下载成功。原因视图里只用了UserFile.objects.get(idfile_id)没有加ownerrequest.user过滤。解决所有涉及具体文件的操作查询条件必须带当前用户或者用get_object_or_404(UserFile, idfile_id, ownerrequest.user)。这是网盘系统最典型也最危险的漏洞写视图时养成「查询必带 owner」的习惯。5.2 上传目录被直接访问media 暴露成静态目录现象知道文件名就能通过/media/xxx直接访问绕过权限校验。原因开发时为了图方便把MEDIA_URL配成静态可访问生产环境没改。解决生产环境media目录不要交给 Nginx 直接location /media/所有文件访问都走 Django 视图做权限判断或者用带签名的临时 URL。本地开发图省事可以放开上线前必须收掉。5.3 大文件上传超时与内存暴涨现象上传几百 MB 的文件要么请求超时要么服务器内存飙升。原因FILE_UPLOAD_MAX_MEMORY_SIZE设得过大或者视图里用了f.read()。解决用f.chunks()分块写盘FILE_UPLOAD_MAX_MEMORY_SIZE保持在合理范围比如 5MB超过的部分 Django 自动落临时文件。Nginx 反代时同步调大client_max_body_size和proxy_read_timeout。5.4 数据库连接数耗尽现象并发一上来就报Too many connections。原因每个请求都新建数据库连接或者用了连接池但配置不当。解决Django 默认每个请求结束后关闭连接CONN_MAX_AGE设为 0 是安全的如果要用持久连接设CONN_MAX_AGE60并配合 Mysql 的max_connections调整。生产环境可以考虑mysql的数据库连接池方案但小规模个人网盘没必要先把CONN_MAX_AGE调对更实际。5.5 文件名与路径穿越现象上传文件名里带../文件被写到media目录之外。原因直接用用户提供的文件名拼路径。解决存储时用uuid重命名原始文件名只存数据库不参与路径拼接。如果一定要用原始名用os.path.basename去掉路径部分并过滤..。这个坑不常遇到但一旦被利用就是服务器文件被覆盖。6. 让网盘更好用几个能立刻加上的进阶技巧基础功能跑通后有几个小改动能明显提升体验而且实现成本不高。第一个是文件列表分页Django 自带Paginator文件多了不分页会一次性查出所有记录页面卡、数据库压力大from django.core.paginator import Paginator def file_list(request): qs UserFile.objects.filter(ownerrequest.user, is_deletedFalse).order_by(-upload_time) paginator Paginator(qs, 20) # 每页 20 条 page paginator.get_page(request.GET.get(page)) return render(request, list.html, {page: page})Paginator的第二个参数是每页条数个人网盘 20 到 50 都合理。模板里用page.has_next、page.next_page_number做翻页控件。第二个是上传进度和断点续传的前置准备。Django 原生不擅长处理大文件分片常见做法是前端用File.slice()切片每片单独 POST后端按file_id chunk_index存临时块全部传完再合并。这个改动量不小但如果你的网盘要传几个 GB 的文件值得做。合并时注意按chunk_index排序别依赖到达顺序。第三个是定期清理过期分享和软删除文件。写一个管理命令用crontab每天跑一次# files/management/commands/clean_expired.py from django.core.management.base import BaseCommand from django.utils import timezone from files.models import ShareLink, UserFile class Command(BaseCommand): def handle(self, *args, **options): ShareLink.objects.filter(expire_at__lttimezone.now()).delete() UserFile.objects.filter(is_deletedTrue).delete()expire_at__lttimezone.now()是 Django ORM 的字段查询语法__lt表示小于。这个命令配合python manage.py clean_expired就能手动执行加到定时任务里自动跑。注意软删除文件直接delete()会连文件本体一起删如果还想保留一段时间改成先标记再延迟清理。我自己维护这套东西最大的教训是别一上来就追求功能全先把上传、下载、权限三件事做扎实再谈分享和回收站。很多网盘源码看着功能多实际权限校验一塌糊涂跑起来能用放出去就出事。环境搭建阶段把字符集、字段类型、上传限制这三个参数定对后面能少熬好几个夜。希望帮到你。本文还有配套的精品资源点击获取
返回列表