项目介绍
在日常工作和生活中,我们经常需要在电脑和手机之间传输文件。传统的传输方式要么需要数据线连接,要么需要借助第三方应用,操作繁琐且不够高效。今天,我将介绍一个基于Django开发的WiFi文件分享应用,它可以让你通过电脑选择本地文件夹,生成访问二维码,然后通过手机扫描二维码即可访问并下载文件,实现快速、便捷的文件共享。
功能特性
– 📁 **文件目录选择**:直观的文件夹选择器,支持浏览并选择本地电脑中的指定文件夹
– 📋 **文件列表展示**:完整显示目录下的所有内容,包括文件和子目录,清晰区分文件类型
– 📱 **手机自适应**:响应式设计,适配各种移动设备
– 📲 **二维码访问**:自动生成包含访问URL的二维码,手机扫描即可快速访问
– 💾 **文件下载**:支持各类常见文件格式的下载
– ⚡ **高性能**:支持大文件传输,文件列表分页加载
– 🔒 **安全可靠**:防止路径遍历攻击,文件大小限制,权限检查
技术栈选择
在开发这个应用时,我选择了以下技术栈:
– **后端框架**:Django 5.0.6 – 成熟稳定的Python Web框架,提供了丰富的内置功能
– **前端技术**:HTML5, CSS3, JavaScript – 基础的Web前端技术栈
– **UI框架**:Bootstrap 5.3.0 – 用于快速构建响应式页面
– **二维码生成**:qrcode库 – 用于在后端生成二维码图片
– **数据库**:SQLite – 轻量级数据库,适合开发和小型应用
核心功能实现
1. 目录选择功能
实现思路
目录选择功能允许用户在电脑上选择一个本地文件夹作为分享目录。为了保证安全性,应用会对用户输入的目录路径进行验证,并转换为绝对路径以防止路径遍历攻击。
代码实现
```python
# file_share/views.py
def home(request):
global SHARED_DIR
if request.method == 'POST':
selected_dir = request.POST.get('directory', '')
try:
# 验证目录是否存在且可访问
if os.path.isdir(selected_dir):
# 获取绝对路径以防止相对路径攻击
abs_path = os.path.abspath(selected_dir)
SHARED_DIR = abs_path
return JsonResponse({'success': True, 'message': '目录选择成功'})
else:
return JsonResponse({'success': False, 'message': '无效的目录路径或无法访问'})
except PermissionError:
return JsonResponse({'success': False, 'message': '没有权限访问该目录'})
except Exception as e:
return JsonResponse({'success': False, 'message': f'发生错误: {str(e)}'})
```
2. 文件列表展示
实现思路
文件列表展示功能用于获取并显示分享目录中的文件和子目录。应用使用`os.scandir()`函数高效遍历目录,并对结果进行分页处理,以提高性能和用户体验。
#### 代码实现
```python
# file_share/views.py
def file_list(request):
global SHARED_DIR
shared_dir = SHARED_DIR
if not shared_dir:
return JsonResponse({'error': '未选择分享目录'})
# 处理分页参数
page = int(request.GET.get('page', 1))
page_size = int(request.GET.get('page_size', 50)) # 默认每页显示50个文件
offset = (page - 1) * page_size
files = []
total = 0
try:
entries = os.scandir(shared_dir)
for entry in entries:
try:
file_info = {
'name': entry.name,
'is_dir': entry.is_dir(),
'path': os.path.relpath(entry.path, shared_dir),
'size': entry.stat().st_size if entry.is_file() else 0,
'mtime': entry.stat().st_mtime
}
files.append(file_info)
total += 1
except PermissionError:
# 跳过没有权限访问的文件/目录
continue
except Exception as e:
# 记录错误但继续处理其他文件
continue
# 分页处理 - 保持文件系统原始顺序
paginated_files = files[offset:offset + page_size]
return JsonResponse({
'files': paginated_files,
'total': total,
'page': page,
'page_size': page_size,
'total_pages': (total + page_size - 1) // page_size
})
except PermissionError:
return JsonResponse({'error': '没有权限访问该目录'})
except Exception as e:
return JsonResponse({'error': f'目录访问错误: {str(e)}'})
```
3. 二维码生成
实现思路
二维码生成功能用于生成包含访问URL的二维码图片。为了确保手机能够正确访问,应用使用了实际的本地IP地址而非localhost,并使用qrcode库在后端生成二维码图片。
#### 代码实现
```python
# file_share/views.py
def get_local_ip():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(('8.8.8.8', 80))
ip = s.getsockname()[0]
finally:
s.close()
return ip
def qrcode_generator(request):
"""生成二维码图片"""
try:
# 使用实际本地IP地址,确保手机可以访问
protocol = request.scheme
hostname = get_local_ip() # 使用实际本地IP地址
port = request.get_port()
url = f"{protocol}://{hostname}:{port}/"
# 生成二维码
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=10,
border=4,
)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image(fill_color="black", back_color="white")
# 将图片转换为HTTP响应
buffer = BytesIO()
img.save(buffer, format="PNG")
buffer.seek(0)
response = HttpResponse(buffer.getvalue(), content_type="image/png")
response["Content-Disposition"] = "inline; filename=qrcode.png"
return response
except Exception as e:
return HttpResponse(f"二维码生成失败: {str(e)}", status=500)
```
4. 文件下载
实现思路
文件下载功能用于将电脑上的文件下载到手机。为了支持大文件下载并提高性能,应用使用了Django的`FileResponse`类,它支持流式传输,不需要将整个文件加载到内存中。
#### 代码实现
```python
# file_share/views.py
# 设置最大文件下载大小为10GB
MAX_DOWNLOAD_SIZE = 10 * 1024 * 1024 * 1024
def download_file(request, file_path):
global SHARED_DIR
shared_dir = SHARED_DIR
if not shared_dir:
return HttpResponse('未选择分享目录', status=400)
try:
# 防止路径遍历攻击
file_path = uri_to_iri(file_path)
full_path = os.path.join(shared_dir, file_path)
full_path = os.path.abspath(full_path)
# 确保文件在分享目录内
if not full_path.startswith(shared_dir):
return HttpResponse('无效的文件路径', status=403)
if not os.path.exists(full_path):
return HttpResponse('文件不存在', status=404)
if os.path.isdir(full_path):
return HttpResponse('不能下载目录', status=400)
# 检查文件大小
file_size = os.path.getsize(full_path)
if file_size > MAX_DOWNLOAD_SIZE:
return HttpResponse(f'文件大小超过限制 ({MAX_DOWNLOAD_SIZE / (1024*1024):.1f}MB)', status=413)
# 使用Django的FileResponse来处理文件下载,这是Django推荐的方式
from django.http import FileResponse
response = FileResponse(open(full_path, 'rb'), as_attachment=True)
response['Content-Disposition'] = f'attachment; filename="{os.path.basename(full_path)}"'
response['Content-Length'] = str(file_size)
return response
except PermissionError:
return HttpResponse('没有权限访问该文件', status=403)
except FileNotFoundError:
return HttpResponse('文件不存在', status=404)
except Exception as e:
return HttpResponse(f'下载失败: {str(e)}', status=500)
```
5. 设备适配
实现思路
为了提供更好的用户体验,应用需要根据访问设备的类型调整UI。当用户使用手机访问时,应该隐藏目录选择区域,只显示二维码和文件列表,提供更简洁的界面。
#### 代码实现
```javascript
// 检测设备类型
function isMobileDevice() {
const userAgent = navigator.userAgent || navigator.vendor || window.opera;
// 检测是否为移动设备
return (/android/i.test(userAgent) ||
/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream);
}
// 控制UI元素显示
function controlUI() {
const isMobile = isMobileDevice();
if (isMobile) {
// 在手机端隐藏目录选择区域
const dirSelectCard = document.querySelector('.card:nth-child(2)');
if (dirSelectCard) {
dirSelectCard.style.display = 'none';
}
}
}
// 初始化
ocument.addEventListener('DOMContentLoaded', () => {
controlUI();
{% if shared_dir %}
loadFileList();
{% endif %}
});
```
技术亮点
1. 安全设计
– **防止路径遍历攻击**:在处理文件路径时,应用会将相对路径转换为绝对路径,并检查文件是否在分享目录内,防止恶意用户访问系统敏感文件
– **文件大小限制**:设置了最大文件下载大小限制,防止过大文件占用过多服务器资源
– **权限检查**:在访问文件和目录时,应用会检查当前用户是否有相应的权限
– **使用Django内置安全功能**:利用Django的CSRF保护、XSS防护等内置安全功能
2. 性能优化
– **分页加载**:文件列表采用分页加载,避免一次性加载大量文件导致性能问题
– **流式传输**:使用FileResponse实现文件的流式传输,支持大文件下载,不需要将整个文件加载到内存中
– **异步请求**:使用JavaScript异步请求加载文件列表,提高页面响应速度
– **合理的错误处理**:在处理文件系统操作时,使用try-except块捕获异常,确保应用不会因为单个文件的问题而崩溃
3. 移动端适配
– **响应式设计**:使用Bootstrap的响应式组件和CSS媒体查询,确保应用在不同屏幕尺寸的设备上都能正常显示
– **设备检测**:通过JavaScript检测访问设备类型,根据设备类型调整UI元素的显示
– **简化移动端界面**:在移动端隐藏不必要的UI元素,只显示核心功能,提供更简洁的用户体验
项目部署
开发环境部署
1. **安装依赖**
```bash
pip install -r requirements.txt
```
2. **数据库迁移**
“`bash
python manage.py migrate
“`
3. **启动开发服务器**
“`bash
python manage.py runserver 0.0.0.0:8000
“`
服务器将在 http://0.0.0.0:8000/ 上运行。
### 生产环境部署
对于生产环境,建议使用以下部署方案:
1. **使用Gunicorn作为WSGI服务器**
“`bash
pip install gunicorn
gunicorn –bind 0.0.0.0:8000 wifi_share.wsgi
“`
2. **使用Nginx作为反向代理**
配置Nginx转发请求到Gunicorn:
“`nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
“`
3. **使用systemd管理服务**
创建systemd服务文件,确保应用在系统启动时自动运行:
“`bash
sudo nano /etc/systemd/system/wifi-share.service
“`
内容如下:
“`
[Unit]
Description=WiFi File Share Django Application
After=network.target
[Service]
User=your_username
Group=www-data
WorkingDirectory=/path/to/your/project
ExecStart=/path/to/your/venv/bin/gunicorn –bind 0.0.0.0:8000 wifi_share.wsgi
Restart=always
[Install]
WantedBy=multi-user.target
“`
启动服务:
“`bash
sudo systemctl start wifi-share
sudo systemctl enable wifi-share
“`
总结与展望
通过这个WiFi文件分享应用的开发,我们学习了如何使用Django框架开发一个实用的Web应用,包括目录选择、文件列表展示、二维码生成、文件下载等核心功能。同时,我们也学习了如何进行安全设计、性能优化和移动端适配,这些都是Web开发中的重要知识点。
未来改进方向
1. **添加文件上传功能**:允许从手机向电脑上传文件
2. **支持子目录浏览**:目前只能显示一级目录,未来可以添加子目录浏览功能
3. **添加文件搜索功能**:支持按文件名搜索文件
4. **添加用户认证**:支持多用户使用,每个用户可以创建自己的分享目录
5. **添加文件预览功能**:支持在浏览器中预览图片、文档等文件
6. **添加进度显示**:为文件下载添加进度条显示
这个应用虽然功能简单,但实用性强,代码结构清晰,适合作为Django学习的实践项目。希望通过本文的介绍,能够帮助大家更好地理解Django的开发流程和核心概念,同时也能够为大家开发类似的应用提供参考。
参考资料
– [Django官方文档](https://docs.djangoproject.com/)
– [Bootstrap官方文档](https://getbootstrap.com/)
– [qrcode库文档](https://pypi.org/project/qrcode/)
– [Python文件操作教程](https://docs.python.org/3/tutorial/inputoutput.html#reading-and-writing-files)

