0 思路
(1) 客户端实现边分片文件边发送。客户端使用生产者消费者模式,生产者用于读取指定大小的二进制文件分片,放入缓存队列中,消费者用于从缓存队列中读取文件分片并post发送到服务器;生产者任务与消费者任务异步进行,使用了协程实现。
(2) 服务端实现边接收文件分片边拼接文件分片。接收并保存文件分片到磁盘用子线程实现,拼接文件分片用子进程实现。需要使用info.ini文件配合实现以上功能,info.ini保存了拼接文件分片的子进程是否存在以及当前需要拼接到合并文件[文件分片合并后的文件]的文件分片id。例如:
[abc.txt]
merge_slice_process_exist=0
curr_merge_slice_id=5
merge_slice_process_exist表示合并分片的子进程是否存在,0表示不存在,1表示存在;curr_merge_slice_id=5表示abc.txt的id为5的文件分片当前需要合并到合并文件中。
1 目录结构

2 代码
# app.py
import logging
from flask import Flask, request, jsonify
from main import ServerBigFileUpload
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/big_file_upload', methods=['GET', 'POST'])
def big_file_upload():
logging.warning('=' * 20 + 'S:0' + '=' * 20)
dir_ = "upload"
return_data = return_data_ = dict()
if request.method == 'GET':
# 0 根据文件名判断文件是否在服务器上已经存在一部分
# 1
logging.warning('=' * 20 + 'S:1' + '=' * 20)
file_name = request.args.get("file_name", -1)
slice_num = int(request.args.get("slice_num", -1))
if slice_num == -1 or file_name == -1:
return_data["message"] = "参数错误"
return_data["status"] = 500
return jsonify(return_data)
# 2 获取已有文件切片的id,返回给客户端,以让客户端决定判断还有哪些切片有待发送
logging.warning('=' * 20 + 'S:2' + '=' * 20)
return_data_ = ServerBigFileUpload.get_main(file_name, slice_num, dir_)
elif request.method == 'POST':
# 3
logging.warning('=' * 20 + 'S:3' + '=' * 20)
file_name = request.args.get("file_name")
slice_id = request.args.get("slice_id")
slice_size = request.args.get("slice_size")
slice_content = request.stream.read()
# 4 接收文件分片并保存到磁盘
logging.warning('=' * 20 + 'S:4' + '=' * 20)
return_data_ = ServerBigFileUpload.post_main(file_name, slice_id, slice_size, slice_content, dir_)
return_data.update(return_data_)
logging.warning('=' * 20 + 'S:5' + '=' * 20)
return jsonify(return_data)
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8888)
# main.py
import os
import threading
import multiprocessing
import configparser
import subprocess
import time
import logging
class ServerBigFileUpload:
@staticmethod
def merge_slice(file_name, slice_num, dir_, curr_merge_slice_id=0, timeout=10, waiting_times=3):
# 2.5.1
logging.warning('=' * 20 + 'S:2.5.1' + '=' * 20)
save_dir = dir_ + "/" + "tmp" + "/" + file_name
info_ini_file = dir_ + '/info.ini'
merge_file = dir_ + "/" + file_name
logging.warning(merge_file)
if not os.path.isfile(merge_file):
open(merge_file, 'a').close()
# 2.5.2 合并后台收到的分片
logging.warning('=' * 20 + 'S:2.5.2' + '=' * 20)
merge_file_open = open(merge_file, "wb+")
waiting_times_ = waiting_times
# # 对于每一个所要合并的分片最多循环等待三次,防止所要合并分片还在传送的路上或还没写入磁盘
while waiting_times_ and curr_merge_slice_id < slice_num:
curr_merge_slice_file = save_dir + "/" + str(curr_merge_slice_id)
if str(curr_merge_slice_id) in os.listdir(save_dir):
with open(curr_merge_slice_file, r"rb") as f:
curr_merge_slice_content = f.read()
merge_file_open.write(curr_merge_slice_content)
os.remove(curr_merge_slice_file) # 调试时可以注释掉
curr_merge_slice_id += 1
waiting_times_ = waiting_times
else:
# 防止所要合并分片还在传送的路上或还没写入磁盘
waiting_times_ -= 1
time.sleep(t

本文档描述了一种大文件分片上传的实现方案。客户端采用生产者消费者模式,将文件分片异步读取并发送到服务器。服务端通过子线程接收并保存文件分片,子进程负责合并文件分片。使用info.ini文件记录合并状态。整个过程兼顾效率与稳定性。
最低0.47元/天 解锁文章
3548

被折叠的 条评论
为什么被折叠?



