要在 Flask 中处理客户端请求 PDF 文件并返回该文件,可以使用 send_file
或 send_from_directory
函数。假设已经有一个 PDF 文件,并且这个文件位于服务器上 static/pdf 目录中,你可以按照以下步骤来实现。
使用 send_file
函数返回 PDF 文件
确保 Flask 已安装并设置好项目结构:
你的项目结构可能如下所示:
/project │ ├── app.py ├── static │ └── pdf │ └── example.pdf └── templates
在
app.py
文件中定义路由来处理 PDF 请求:from flask import Flask, send_file, abort app = Flask(__name__) @app.route('/download/<filename>') def download(filename): # 指定 PDF 文件所在的目录 directory = f'static/files/' # 构造完整的文件路径 file_path = os.path.join(directory, filename) # 检查文件是否存在 if not os.path.isfile(file_path): return "File not found", 404 # 使用 send_file 返回文件 return send_file(file_path, as_attachment=True, download_name=filename) if __name__ == '__main__': app.run(debug=True)
确保客户端向正确的 URL 发送请求:
例如,如果你想下载
example.pdf
文件,客户端应向以下 URL 发送请求:http://127.0.0.1:5000/download/example.pdf
关键点
send_file(file_path, as_attachment=True)
函数用于发送文件,并通过as_attachment=True
强制客户端下载文件而不是直接在浏览器中打开。- 应答报文头中的 Content-Type,Content-Disposition,Content-Length,都会自动设置,这样在服务器段提供下载文件确实比较方便。否在就可以在服务器段,打开文件,自行构造应答:
# 读取文件内容 with open(filepath, 'rb') as f: file_data = f.read() # 构建响应对象 response = Response(file_data, mimetype='application/pdf') response.headers['Content-Disposition'] = f'attachment; filename={filename}' return response
如果遇到大文件怎么办?
当你需要发送特别大的文件时,使用 send_file
仍然是可行的。Flask 的 send_file
函数底层是基于 Python 的 werkzeug
库,它支持文件的流式传输,这意味着文件不会一次性全部加载到内存中,而是逐块传输给客户端。这种方式非常适合发送大文件,因为它减少了内存的占用。
确保文件流式传输:
默认情况下,send_file
会使用wsgi.file_wrapper
来进行文件的流式传输,只要 Web 服务器支持。你不需要做额外的工作,Flask 会自动处理大文件的传输。使用流模式 (Generator) 处理更复杂的需求:
如果你有更复杂的需求,或者你想控制每次传输的块大小,可以考虑使用生成器(Generator)来手动流式传输文件内容。from flask import Flask, Response, abort app = Flask(__name__) def generate_large_file(filepath): with open(filepath, 'rb') as f: while True: data = f.read(8192) # 每次读取8KB if not data: break yield data @app.route('/download/<filename>') def download_file(filename): filepath = f'static/files/{filename}' try: return Response(generate_large_file(filepath), mimetype='application/pdf', # 这里需要手动填充应答报文头 headers={'Content-Disposition': f'attachment; filename={filename}'}) except FileNotFoundError: abort(404) if __name__ == '__main__': app.run(debug=True)
通常情况下,send_file
足够应对大多数情况。
使用生成器模式来流式传输大文件时,客户端通常不需要进行特别的处理。生成器模式对客户端来说是透明的,客户端仍然会像处理普通文件下载一样处理这个请求。
客户端可以直接下载文件并存储到本地。由于文件是分块传输的,客户端可能会逐渐接收文件,如果网络或服务器较慢,客户端可能会看到下载速度较慢,但文件仍然会被完整下载。
无论是浏览器、Python 脚本、或者其他 HTTP 客户端,通常都不需要对生成器模式的下载进行特别处理。下载流程与普通的文件下载保持一致:
假如你使用 Python 的 requests
库来下载文件,也无需做特殊处理。requests
库会自动逐块接收文件。示例代码如下:
import requests url = 'http://127.0.0.1:5000/download/largefile.pdf' local_filename = 'downloaded_largefile.pdf' with requests.get(url, stream=True) as r: r.raise_for_status() with open(local_filename, 'wb') as f: for chunk in r.iter_content(chunk_size=8192): f.write(chunk)
在这个例子中,stream=True
允许 requests
库以流模式下载文件,逐块写入本地文件。
- 断点续传: 如果你希望支持断点续传,服务器和客户端需要更复杂的处理,例如通过
Range
头部来指定下载的字节范围。这种情况可能需要更多的定制和处理。