下面是关于Python+django实现文件下载的完整攻略以及两条示例说明。
Django是一个Python Web框架,采用了MVC的软件架构模式,是一个全功能的高级Web框架。Django的官方网站是djangoproject.com。
在Django中,可以通过编写视图函数实现文件的下载。
首先,在项目的urls.py文件中添加文件下载的路由。假设我们要下载的文件名为 test.zip,我们可以将这个路由设置为/downloads/test:
from django.urls import path
from . import views
urlpatterns = [
# ... 其他路由 ...
path('downloads/<str:filename>', views.download_file, name='download_file'),
]
接下来,我们需要定义视图函数来处理/downloads/
import os
from django.http import HttpResponse
def download_file(request, filename):
# 获取文件路径
file_path = os.path.join(settings.BASE_DIR, 'downloads', filename)
# 判断文件是否存在
if os.path.exists(file_path):
# 打开文件
with open(file_path, 'rb') as f:
# 构造HttpResponse对象,并设置文件头
response = HttpResponse(f.read())
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename=' + filename
return response
else:
return HttpResponse('File not found')
该函数从request对象中获取了文件名参数filename,然后通过os模块拼接出了文件的绝对路径。如果文件存在,函数就打开文件并构造HttpResponse对象,并设置Content-Type和Content-Disposition这两个文件头,最后将HttpResponse对象返回。如果文件不存在,函数会返回一个字符串“File not found”。
在以上的视图函数中,我们通过os模块拼接出了文件的绝对路径。在这个例子中,我们将文件存储在了项目目录的downloads文件夹下:
myproject/
downloads/
test.zip
myproject/
settings.py
urls.py
views.py
...
如果需要动态生成文件并下载,我们可以在视图函数中通过Python生成文件,然后以文件流的方式向HttpResponse对象中写入。
首先,我们需要在models.py中创建一个模型,用于保存需要生成的数据。例如,我们可以创建一个叫做FileData的模型:
from django.db import models
from django.utils import timezone
class FileData(models.Model):
filename = models.CharField(max_length=255, unique=True)
data = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
然后,我们需要创建一个生成文件的函数。以生成CSV文件为例:
import csv
def generate_csv(filedata):
# 将data字段中的字符串解析为二维列表
data = list(csv.reader(filedata.data.splitlines()))
# 打开文件
fp = open(filedata.filename, 'w', newline='', encoding='utf-8')
# 写入文件内容
writer = csv.writer(fp)
for row in data:
writer.writerow(row)
# 关闭文件
fp.close()
该函数接收一个FileData对象作为参数,并将该对象的data字段中的字符串解析为二维列表,然后通过csv模块将该二维列表写入到文件中。
接下来,我们需要定义视图函数来处理/downloads/
import os
import csv
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from .models import FileData
def download_csv(request, pk):
# 获取要下载的FileData对象
filedata = get_object_or_404(FileData, pk=pk)
# 生成CSV文件并获取文件路径
generate_csv(filedata)
file_path = filedata.filename
# 打开文件
with open(file_path, 'rb') as f:
# 构造HttpResponse对象并设置文件头
response = HttpResponse(f.read())
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename=' + filedata.filename
# 返回HttpResponse对象
return response
该函数接收一个FileData的主键作为参数pk,并通过get_object_or_404函数获取到对应的FileData对象。然后,该函数通过generate_csv函数生成CSV文件,并在HttpResponse对象中设置Content-Type和Content-Disposition这两个文件头,最后将HttpResponse对象返回。
最后,我们需要在视图函数中创建一个FileData对象,将其保存到数据库中,并且将其主键作为下载路由的参数。例如:
from django.urls import path
from . import views
urlpatterns = [
# ... 其他路由 ...
path('downloads/csv/<int:pk>/', views.download_csv, name='download_csv'),
]
然后在views.py中创建一个视图函数,用来生成FileData对象并保存到数据库:
def save_csv(request):
# 生成CSV数据
data = [
['Name', 'Age', 'Email'],
['Jack', '24', 'jack@example.com'],
['Mary', '30', 'mary@example.com'],
['Tom', '18', 'tom@example.com'],
]
# 创建FileData对象并保存到数据库
filedata = FileData(filename='test.csv', data=csv.writer(data).getvalue())
filedata.save()
# 重定向到下载视图
return redirect('download_csv', pk=filedata.pk)
该函数生成CSV数据,然后创建FileData对象,并将其保存到数据库中。最后,该函数通过redirect函数将页面重定向到/download/csv/
如果被下载的文件非常大,那么将其读入内存可能会导致内存溢出。为了避免这个问题,我们可以对文件进行分块读取。例如,以下是一个读取文件并以分块的方式返回HttpResponse对象的示例代码:
with open(file_path, 'rb') as f:
response = StreamingHttpResponse(iter(lambda: f.read(4096), b''))
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename=' + filename
return response
这里的iter函数会将文件按照每次读取4096个字节的方式进行分块,然后将每一块返回给HttpResponse对象。
在模板中,我们可以使用url函数来生成下载链接。例如,以下是一个在模板中生成下载链接并添加到表格中的示例代码:
<tr>
<td>{{ filedata.filename }}</td>
<td><a href="{% url 'download_file' filename=filedata.filename %}">下载</a></td>
</tr>
该代码中的{% url 'download_file' filename=filedata.filename %}会生成一个下载链接,并将文件名作为参数传递给视图函数download_file。
本文链接:http://task.lmcjl.com/news/16119.html