使用Python實(shí)現(xiàn)大文件切片上傳及斷點(diǎn)續(xù)傳的方法
概要
本文使用python實(shí)現(xiàn)大文件切片上傳,并支持?jǐn)帱c(diǎn)續(xù)傳,將功能劃分為:獲取上傳文件接口狀態(tài)、獲取臨時(shí)文件夾狀態(tài)信息、切片上傳、切片合并四個(gè)功能模塊。提供了詳細(xì)的思路和功能源碼。
整體架構(gòu)流程
步驟示例: 1.前端收到用戶的上傳的文件后,會(huì)執(zhí)行以下操作:
- 計(jì)算文件哈希值:通過特定算法,算出該文件的hash值。
- 向后端傳參:將文件路徑(包括文件名)、目標(biāo)機(jī)器id、文件hash值、用戶id,一并傳遞給后端判斷文件狀態(tài)接口get_file_upload_status。
后端收到前端傳來的數(shù)據(jù)后,按如下邏輯進(jìn)行處理: 檢查文件是否已經(jīng)存在:判斷該文件是否存在,如果已存在,則返回前端讓用戶選擇是否覆蓋文件。 檢查Redis中上傳記錄:如果不存在則進(jìn)一步判斷redis中是否有該文件正在上傳的記錄。
- 無上傳記錄:如果沒有則使用redis String數(shù)據(jù)類型進(jìn)行存儲(chǔ)文件上傳狀態(tài),key為"機(jī)器_id + 文件路徑(例如118/tmp/123.jpg)",value為自制一個(gè)
{
'hash_code': 文件hash值,
'person_id': 用戶_id
}
- 有上傳記錄:如果redis中存在正在上傳的記錄,則比對(duì)用戶id是否為同一個(gè)人,是則可以斷點(diǎn)續(xù)傳,否則阻止其他人上傳。
2.當(dāng)文件狀態(tài)通過后,前端會(huì)調(diào)用get_file_dir_info接口,并傳入主機(jī)_id、用戶_id、文件路徑(包括文件名)。 get_file_dir_info接口主要有三個(gè)功能:
- 檢查臨時(shí)文件夾:判斷用于存放切片的 “臨時(shí)文件夾” 是否存在。臨時(shí)文件夾的完整路徑由 “文件路徑 + : + 用戶 id” 拼接而成,例如 “/tmp/123.jgp:12”。若該文件夾不存在,系統(tǒng)會(huì)自動(dòng)創(chuàng)建它。創(chuàng)建完成后,對(duì)臨時(shí)文件夾路徑進(jìn)行哈希計(jì)算,再借助 Redis 存儲(chǔ)相關(guān)信息,以哈希值作為 key,臨時(shí)文件夾的全路徑作為 value,方便后續(xù)執(zhí)行刪除文件夾操作時(shí)快速定位。
- 檢查切片上傳記錄臨時(shí)文件:查看 “臨時(shí)文件夾” 下的 “切片上傳記錄臨時(shí)文件” 是否已經(jīng)存在。如果文件存在,就讀取其中的內(nèi)容;要是不存在,就對(duì)其內(nèi)容進(jìn)行初始化。“臨時(shí)記錄文件”內(nèi)容如下:
class FileObject():
def __init__(self,name,size,chunk,path=None,num=0):
self.name = name #文件名
self.size = size #文件夾已上傳大小
self.chunk = chunk #塊數(shù)
self.path = path # 路徑
self.num = num #文件夾下存在分塊數(shù),設(shè)計(jì)冗余了
- 放置合并腳本:在臨時(shí)文件夾下放置 merge.py 腳本文件,為后續(xù)的文件處理流程提供支持。
最后遍歷整個(gè)臨時(shí)文件夾,獲得一個(gè)文件已上傳塊數(shù)的列表(為了支持?jǐn)帱c(diǎn)續(xù)傳),返回給前端臨時(shí)記錄文件信息、臨時(shí)文件夾的hash值、已上傳的文件塊列表。
3.前端將文件按照10M進(jìn)行切片,并按照上述接口得到的信息(文件名、已上傳的文件塊列表),進(jìn)行續(xù)傳或直接傳輸file_chunk_upload,通過列表中的信息可跳過已上傳的文件塊,并可補(bǔ)上中間因?yàn)槠渌騺G失的塊。 4.上傳完成后前端會(huì)再次調(diào)用get_file_dir_info接口,獲得當(dāng)前文件夾下已存在的文件塊數(shù)量與列表,與自己分塊進(jìn)行對(duì)比。如果成功則調(diào)用合并文件接口。 5.前端發(fā)起合并請(qǐng)求時(shí),會(huì)調(diào)用 bigfile_merge 接口,同時(shí)傳入用戶 id、文件塊數(shù)、文件路徑以及臨時(shí)文件夾路徑這些關(guān)鍵參數(shù)。 后端在接收到前端傳來的數(shù)據(jù)后,展開一系列校驗(yàn)與操作:
- 參數(shù)校驗(yàn):對(duì)傳入的路徑與文件塊數(shù)進(jìn)行準(zhǔn)確性核查,如果正確則開始合并。
- 合并方式選擇:合并功能主要分兩種執(zhí)行路徑。一是檢測(cè)目標(biāo)機(jī)器是否支持運(yùn)行 merge.py 腳本,若支持,便直接執(zhí)行已傳入的腳本完成合并;若不支持,則執(zhí)行目標(biāo)機(jī)器文件的操作方法來執(zhí)行合并。
- 斷點(diǎn)續(xù)合判斷:開始合并前,要先確認(rèn)需要合并的文件是否已經(jīng)存在。若文件已存在,意味著這是一個(gè)斷點(diǎn)文件,此時(shí)需讀取臨時(shí)記錄文件中的 size 和 chunk 值,從而判斷文件的實(shí)際大小以及已合并的塊位置。一旦發(fā)現(xiàn)實(shí)際文件大小超過記錄文件中的數(shù)值,就表明合并過程曾出現(xiàn)中斷,此時(shí)要把文件指針調(diào)整到記錄值對(duì)應(yīng)的位置,接著從該記錄值對(duì)應(yīng)的塊開啟后續(xù)合并。
- 記錄更新 :每成功完成一塊文件的合并,就把新的 size 與 chunk 值寫入臨時(shí)記錄文件。
- 收尾清理:合并完成后清除臨時(shí)文件夾,并刪除redis中該文件上傳記錄狀態(tài)信息。
到此完成一次文件上傳操作
技術(shù)細(xì)節(jié)
- API
獲取上傳文件狀態(tài)接口
get_file_upload_status 查看需要上傳的文件是否存在或斷點(diǎn)續(xù)
class FileUploadStatus(APIView):
def post(self, request, *args, **kwargs):
#先判斷目標(biāo)地址下是否存在該文件
#如果存在則提示是否需要覆蓋
try:
forms = json.loads(request.body)
host_id = forms['host_id']
full_path = forms['full_path']
hash_code = forms['hash_code']
person_id = forms['person_id']
except Exception as e:
logger.error("前端參數(shù)解析出錯(cuò)",repr(e))
try:
err, tmp_sftp = RemoteBigFileUploadHandler.get_handle(host_id) #使用paramiko模塊獲取目標(biāo)機(jī)器連接,這里讀者自行用其他方式都可以
if err:
return Response({'code': -1, 'data': host_id, 'msg': "連接目標(biāo)主機(jī)出錯(cuò)"}, status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
logger.error("連接目標(biāo)機(jī)器失敗",repr(e))
try:
#判斷機(jī)器上文件是否存在
tmp_sftp._sftp.stat(full_path)
return Response({'code': -1, 'data': full_path, 'msg': "文件已存在,通知是否覆蓋"}, status=status.HTTP_400_BAD_REQUEST)
except IOError:
#其次判斷是否存在在redis中,防止短時(shí)間內(nèi)有相同的人傳同一份文件,但前一個(gè)人并沒有合并完成
#如果都沒有則創(chuàng)建新key hashcode放入redis,并通知前端可以上傳
try:
r = get_redis() #獲得redis連接實(shí)例,讀者自行實(shí)現(xiàn)
except Exception as e:
logger.error("redis獲取失敗",repr(e))
#例如:18115/app/home/202.jpg
#獲取redis中記錄
data = r.get(str(host_id)+full_path)
if not data:
#將key和hashcode放入redis
#加上person_id
file_dict = {}
file_dict['hash_code'] = hash_code
file_dict['person_id'] = person_id
r.set(str(host_id)+full_path, str(file_dict))
return Response({'code': 1, 'data': full_path, 'msg': "文件不存在,可以上傳"}, status=status.HTTP_200_OK)
else:
#如果redis中有記錄,host_id+路徑能夠攔截在目標(biāo)機(jī)器傳輸同一份文件,但也會(huì)把同一個(gè)人想斷點(diǎn)傳輸攔截
#所以在此key的value中保存帶有person_id的臨時(shí)文件夾路徑信息
#使用person_id將同一個(gè)人放行
retrieved_data = r.get(str(host_id)+full_path)
# 將獲取到的數(shù)據(jù)轉(zhuǎn)換為字典對(duì)象
try:
retrieved_dict = eval(retrieved_data.decode())
except Exception as e:
logger.error("redis獲取目標(biāo)文件hashcode解碼失敗",repr(e))
if person_id ==retrieved_dict['person_id']:
return Response({'code': 1, 'data': full_path, 'msg': "斷點(diǎn)續(xù)傳判斷通過"}, status=status.HTTP_200_OK)
else:
return Response({'code': 2, 'data': full_path, 'msg': "該文件正在上傳,請(qǐng)勿多次操作"}, status=status.HTTP_200_OK)
獲取臨時(shí)文件夾狀態(tài)信息接口
get_file_dir_info 獲取臨時(shí)文件夾狀態(tài)信息接口
class GetFileDirInfo(APIView):
def post(self, request, *args, **kwargs):
forms = json.loads(request.body)
host_id = forms['host_id']
person_id = forms['person_id']
full_path = forms['full_path']
err, tmp_sftp = RemoteBigFileUploadHandler.get_handle(host_id)#使用paramiko模塊獲取目標(biāo)機(jī)器連接,這里讀者自行用其他方式都可以
if err:
return Response({'code': -1, 'data': host_id, 'msg': "連接目標(biāo)主機(jī)出錯(cuò)"}, status=status.HTTP_400_BAD_REQUEST)
#分離路徑名與文件名
#示例:/home/app 2-2.jpg
file_path,file_name = os.path.split(full_path)
if file_path[-1] != "/":
file_path += "/"
#分離文件名與文件擴(kuò)展名
#示例:2-2 .jpg
short_name,ext = os.path.splitext(file_name)
#構(gòu)造存放臨時(shí)文件的文件夾名
#示例:/home/app/2-2.jpg:12/
tmp_dir = full_path +":"+ str(person_id)
#并將臨時(shí)文件夾做hashcode 傳入redis key為hashcode value為路徑,后續(xù)刪除文件夾操作多一層驗(yàn)證
tmp = hashlib.md5()
tmp.update(tmp_dir.encode('utf-8'))
hash_value = tmp.hexdigest()
if tmp_dir[-1] != "/":
tmp_dir += "/"
try:
#判斷臨時(shí)文件夾是否存在
tmp_sftp._sftp.stat(tmp_dir)
except Exception:
try:
print('創(chuàng)建臨時(shí)文件夾')
tmp_sftp._sftp.mkdir(tmp_dir)
r = get_redis() #獲取redis連接
r.set(hash_value,tmp_dir)
except Exception as e:
logger.error("創(chuàng)建臨時(shí)文件夾失敗",e)
tmp_sftp._sftp.close()
return Response({'code': -1, 'data': "", 'msg': "創(chuàng)建臨時(shí)文件夾失敗"}, status=status.HTTP_400_BAD_REQUEST)
#如果臨時(shí)文件夾存在則在此文件夾下獲取文件傳輸txt
#初始化記錄文件對(duì)象
fobject = FileObject(file_name,0,0,tmp_dir,0)
#示例:/home/app/2-2.jpg:12/2-2.txt
record_file = tmp_dir+short_name+".txt"
try:
#判斷記錄文件是否存在
tmp_sftp._sftp.stat(record_file)
tmp_load_file = tmp_sftp._sftp.open(record_file,'r')
#如果記錄文件夾存在則讀取其已保存上傳文件大小和塊數(shù)
for line in tmp_load_file.readlines():
title,value = line.replace('\n','').split(':')
print(title + ":"+ value)
if title=='name':
fobject.name = value
elif title=='size':
fobject.size = int(value)
elif title =='chunk':
fobject.chunk = int(value)
tmp_load_file.close()
except IOError:
print("記錄文件不存在,創(chuàng)建新的記錄文件,并初始化")
store_f=tmp_sftp._sftp.open(record_file,'w+')
store_f.writelines(['name:'+fobject.name+'\n','size:'+str(fobject.size)+'\n',
'chunk:'+str(fobject.chunk)+'\n'])
print("創(chuàng)建記錄文件成功")
store_f.close()
serializer = RecordFileSerializer(fobject)
data = serializer.data
data['dir_hashcode'] = hash_value
#上傳一個(gè)支持異步合并的merge.py文件
command = 'command -v python'
stdin, stdout, stderr = tmp_sftp._ssh.exec_command(command)
output = stdout.read().decode().strip()
try:
print('判斷merge在不在')
tmp_sftp._sftp.stat(tmp_dir + 'merge.py')
except Exception as e:
if output:
print('目標(biāo)機(jī)器已安裝 Python')
current_path = os.path.dirname(os.path.abspath(__file__))
if current_path[-1] != "/":
current_path += "/"
try:
print('需要傳輸?shù)牡刂?,current_path+'merge.py')
print('目標(biāo)地址',tmp_dir + 'merge.py')
tmp_sftp._sftp.put(current_path+'merge.py',tmp_dir + 'merge.py')
except Exception as e:
logger.error("發(fā)送本地合并文件失敗",e)
tmp_sftp._sftp.close()
return Response({'code': -1, 'data': "", 'msg': "發(fā)送本地合并文件失敗"}, status=status.HTTP_400_BAD_REQUEST)
#將merge文件發(fā)送
else:
tmp_sftp._sftp.close()
return Response({'code': -1, 'data': "", 'msg': "目標(biāo)機(jī)器未安裝python"}, status=status.HTTP_400_BAD_REQUEST)
tmp_sftp._sftp.close()
return Response({'code': 0, 'data': data, 'msg': "創(chuàng)建新記錄文件"}, status=status.HTTP_200_OK)
file_list = tmp_sftp._sftp.listdir(tmp_dir)
#文件夾下有一個(gè)txt記錄文件,總文件塊數(shù)需要減一
fobject.num = len(file_list)-2
chunk_list =[]
for file in file_list:
parts = file.rsplit(":", 1) # 按冒號(hào)從右到左分割為最多兩個(gè)部分
if len(parts) == 2:
last_part = parts[1] # 獲取最后一個(gè)部分
try:
chunk_list.append(int(last_part))
except Exception as e:
continue
print("最后一個(gè)值:", last_part)
else:
print("未找到冒號(hào),跳過")
serializer = RecordFileSerializer(fobject)
data = serializer.data
data['dir_hashcode'] = hash_value
data['chunk_list'] = chunk_list
return Response({'code': 0, 'data': data, 'msg': "記錄文件已存在"}, status=status.HTTP_200_OK)
切片上傳功能
file_chunk_upload 切片上傳功能
class FileChunkUploadHandler():
def __init__(self, redis_host,request=None,file_info=None,chunk_num=0,people_id =0):
self.request = request
self.file_info = request.FILES.get('file_info')
# forms = json.loads(request.body)
#數(shù)據(jù)塊信息
#第幾個(gè)數(shù)據(jù)塊
self.chunk_num = request.data.get('chunk_num')
self.full_path = request.data.get('full_path')
self.person_id = request.data.get('person_id')
self.redis_host = redis_host
def upload_file(self):
print("%s號(hào)文件塊開始傳輸",self.chunk_num)
full_path = urllib.parse.unquote(self.full_path)
file_path,file_name = os.path.split(full_path)
host_id = self.request.query_params.get('host_id', None)
err, tmp_sftp = RemoteBigFileUploadHandler.get_handle(host_id)
#分離擴(kuò)展名和文件名
#2-2 .jpg
short_name,ext = os.path.splitext(file_name)
if file_path[-1] != "/":
file_path += "/"
#存放臨時(shí)文件的文件夾,在上一個(gè)獲取臨時(shí)文件夾狀態(tài)接口就已創(chuàng)建,按照相同格式做一次校驗(yàn)。
#/home/app/2-2.jpg:12
tmp_dir = file_path+file_name+":"+ str(self.person_id)
if tmp_dir[-1] != "/":
tmp_dir += "/"
#臨時(shí)文件名
#/home/app/2-2.jpg:12/12-2-2:1
tmp_file_name = tmp_dir + str(self.person_id) + "-" + short_name + ":" + str(self.chunk_num)
try:
tmp_sftp._sftp.stat(tmp_dir)
except IOError as e:
logger.error("存儲(chǔ)臨時(shí)文件夾不存在",e)
return 0,repr(e)
try:
tmp_sftp._sftp.stat(tmp_file_name)
except IOError:
try:
#文件塊上傳存放
print('創(chuàng)建臨時(shí)文件塊',tmp_file_name)
remote_file = tmp_sftp._sftp.open(tmp_file_name, mode="wb")
my_bytes = self.file_info.read()
remote_file.write(my_bytes)
remote_file.close()
except Exception as e:
logger.error("上傳文件塊失敗",e)
return 0,repr(e)
print("寫入文件完成:",tmp_file_name)
record_file = tmp_dir+short_name+".txt"
#更新上傳記錄文件信息
fobject = FileObject(file_name,0,0)
tmp_load_file = tmp_sftp._sftp.open(record_file,'r')
for line in tmp_load_file.readlines():
title,value = line.replace('\n','').split(':')
print(title + ":"+ value)
if title=='name':
fobject.name = value
elif title=='size':
fobject.size = int(value)
elif title =='chunk':
fobject.chunk = int(value)
tmp_load_file.close()
try:
tmp_sftp._sftp.stat(record_file)
load_file = tmp_sftp._sftp.open(record_file,'w+')
load_file.writelines(['name:'+fobject.name+'\n','size:'+str(fobject.size)+'\n',
'chunk:'+str(0)+'\n'])
except Exception as e:
logger.error(e)
tmp_sftp.close()
return 1,None
文件合并功能
bigfile_merge 文件合并與校驗(yàn)功能
class RemoteBigFileUploadHandler():
def __init__(self, redis_host,request=None, chat=False,tmp_path=None,chunk_num=0,person_id=0):
self.request = request
forms = json.loads(request.body)
self.chat = chat
#數(shù)據(jù)塊大小
# self.chunk_size = forms['chunk_size']
self.tmp_path = forms['tmp_path']
#數(shù)據(jù)塊數(shù)量
self.chunk_num = forms['chunk_num']
self.full_path = forms['full_path']
self.person_id = forms['person_id']
self.redis_host = redis_host
if self.chat:
self.current = 0
self.datetime = datetime.datetime.now()
#數(shù)據(jù)進(jìn)度聊天室
self.channel_layer = get_channel_layer()
self.room = self.request.query_params.get('chat_room', None)
self.total = self.request.query_params.get('total', None)
if not self.room or not self.total:
raise KeyError('params: chat_room and total is needed')
def file_isavailable(self):
tmp_path = self.tmp_path
tmp_sftp = self.get_session()
file_list = tmp_sftp._sftp.listdir(tmp_path)
#文件夾下有一個(gè)txt記錄文件,總文件塊數(shù)需要減一
true_num = len(file_list)-2
if true_num!=self.chunk_num:
tmp_sftp._sftp.close()
return False
else:
tmp_sftp._sftp.close()
return True
def merge_file(self):
#----------------------獲得基礎(chǔ)信息-----------------------------------
host_id = self.request.query_params.get('host_id', None)
full_path = urllib.parse.unquote(self.full_path)
file_path,file_name = os.path.split(full_path)
if file_path[-1] != "/":
file_path += "/"
#分離擴(kuò)展名和文件名
short_name,ext = os.path.splitext(file_name)
tmp_path = self.tmp_path
tmp_sftp = self.get_session()
file_list = tmp_sftp._sftp.listdir(tmp_path)
try:
tmp_sftp._sftp.stat(full_path)
print("文件已存在")
except IOError:
print("文件不存在")
print("創(chuàng)建新的文件")
new_f = tmp_sftp._sftp.open(full_path,mode='a')
new_f.write("")
new_f.close()
#判斷merge.py 文件是否存在
if 'merge.py' in file_list:
com_path = tmp_path + 'merge.py'
command = f"nohup python {com_path} {self.person_id} {self.chunk_num} &"
stdin, stdout, stderr = tmp_sftp._ssh.exec_command(command)
merging = True
while merging:
if self.chat:
# websocket回顯進(jìn)度,獲得當(dāng)前文件字節(jié)數(shù)
self.current = tmp_sftp._sftp.stat(full_path).st_size
print('current',self.current)
print('total',self.total)
self.call_back()
if self.current >= int(self.total):
merging = False
time.sleep(1)
else :
merging = False
else:
#創(chuàng)建或打開用戶的存儲(chǔ)文件
# ----------------開始合并操作-------------------------------
# rb+ 追加覆蓋的方式打開用戶需要存儲(chǔ)文件,指針初始位置為文件開頭
remote_file = tmp_sftp._sftp.open(full_path,mode='rb+')
#默認(rèn)當(dāng)前塊數(shù)從1開始
i=1
#創(chuàng)建文件記錄對(duì)象
fobject = FileObject(file_name,0,0)
print('創(chuàng)建記錄對(duì)象成功')
# 斷點(diǎn)續(xù)傳記錄文件
record_file = tmp_path+short_name+".txt"
try :
#如果記錄文件存在,則存儲(chǔ)文件大概率也存在
#如果兩個(gè)文件打開出錯(cuò) 則走excpt重新創(chuàng)建
tmp_sftp._sftp.stat(record_file)
upload_file = tmp_sftp._sftp.stat(full_path)
#這里也是獲取本地文件的大小
temp_size = upload_file.st_size
print("記錄文件已存在")
print("當(dāng)前文件真實(shí)數(shù)據(jù)大?。?,temp_size)
# 讀取文件,獲得記錄的已上傳塊數(shù),以及大小總塊數(shù)
tmp_load_file = tmp_sftp._sftp.open(record_file,'r')
for line in tmp_load_file.readlines():
title,value = line.replace('\n','').split(':')
print(title + ":"+ value)
if title=='name':
fobject.name = value
elif title=='size':
fobject.size = int(value)
elif title =='chunk':
fobject.chunk = int(value)
tmp_load_file.close()
except IOError:
print("記錄文件不存在,創(chuàng)建新的記錄文件,并初始化")
temp_size = 0
store_f=tmp_sftp._sftp.open(record_file,'w+')
store_f.writelines(['name:'+fobject.name+'\n','size:'+str(fobject.size)+'\n',
'chunk:'+str(fobject.chunk)+'\n'])
print("創(chuàng)建記錄文件成功")
store_f.close()
# ----------------------------------開始保存數(shù)據(jù)--------------------------
print("真實(shí)文件數(shù)據(jù)大小",temp_size)
print("記錄文件數(shù)據(jù)大小",fobject.size)
if temp_size>=fobject.size:
#如果實(shí)際文件大于記錄文件,則表示在合并時(shí)中斷過
print("調(diào)整指針")
remote_file.seek(fobject.size)
i = fobject.chunk+1
tmp_file_name = str(self.person_id) + "-" + short_name + ":"
try:
while i<=self.chunk_num:
if tmp_file_name + str(i) in file_list:
print('true')
#臨時(shí)文件夾/臨時(shí)文件名
file_path = tmp_path+tmp_file_name + str(i)
with tmp_sftp._sftp.file(file_path,'rb') as input_file:
print(file_path)
file_content = input_file.read()
remote_file.write(file_content)
fobject.size += len(file_content)
i = i+1
fobject.chunk += 1
if self.chat:
# websocket回顯進(jìn)度,獲得當(dāng)前文件字節(jié)數(shù)
self.current = tmp_sftp._sftp.stat(full_path).st_size
print('current',self.current)
print('total',self.total)
self.call_back()
#將已經(jīng)寫入的塊和大小記錄
load_file = tmp_sftp._sftp.open(record_file,'w+')
load_file.writelines(['name:'+fobject.name+'\n','size:'+str(fobject.size)+'\n',
'chunk:'+str(fobject.chunk)+'\n'])
load_file.close()
except Exception as e:
logger.error("合并文件異常",e)
#----------------清除臨時(shí)文件與關(guān)閉資源操作----------
#刪除臨時(shí)文件夾下的所有文件
remote_file.close()
try:
for name in file_list:
del_path = tmp_path + name
tmp_sftp._sftp.remove(del_path)
# 刪除空臨時(shí)文件夾
tmp_sftp._sftp.rmdir(tmp_path)
except Exception as e:
logger.error("刪除文件文件異常",e)
#關(guān)閉操作文件資源
tmp_sftp._sftp.close()
#刪除redis中該文件臨時(shí)記錄,一個(gè)是文件夾hashcode,用于驗(yàn)證與刪除目標(biāo)文件夾
#一個(gè)是host_id+路徑,用于標(biāo)記該路徑下這個(gè)文件正在傳輸
r = self.redis_host
data = r.get(str(host_id)+full_path)
if data:
r.delete(str(host_id)+full_path)
tmp = hashlib.md5()
tmp.update(tmp_path.encode('utf-8'))
hash_value = tmp.hexdigest()
r.delete(hash_value)
print("文件合并結(jié)束")
return 1,None
小結(jié)
本文提供了一個(gè)python大文件上傳功能實(shí)現(xiàn)的思路,希望能夠你帶來一些思路和啟發(fā)。
以上就是使用Python實(shí)現(xiàn)大文件切片上傳及斷點(diǎn)續(xù)傳的方法的詳細(xì)內(nèi)容,更多關(guān)于Python大文件切片上傳及斷點(diǎn)續(xù)傳的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 詳解Python中的斷點(diǎn)類型
- python實(shí)現(xiàn)斷點(diǎn)調(diào)試的方法
- 基于python實(shí)現(xiàn)Pycharm斷點(diǎn)調(diào)試
- python 文件下載之?dāng)帱c(diǎn)續(xù)傳的實(shí)現(xiàn)
- Python PyCharm如何進(jìn)行斷點(diǎn)調(diào)試
- python3射線法判斷點(diǎn)是否在多邊形內(nèi)
- 利用Pycharm斷點(diǎn)調(diào)試Python程序的方法
- python實(shí)現(xiàn)可以斷點(diǎn)續(xù)傳和并發(fā)的ftp程序
- python支持?jǐn)帱c(diǎn)續(xù)傳的多線程下載示例
- python斷點(diǎn)測(cè)試的實(shí)現(xiàn)
相關(guān)文章
python 用 xlwings 庫(kù) 生成圖表的操作方法
這篇文章主要介紹了python 用 xlwings 庫(kù) 生成圖表的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-12-12
Python報(bào)錯(cuò)ValueError:?cannot?convert?float?NaN?to?intege
在Python編程中,我們經(jīng)常需要處理各種數(shù)據(jù)類型,包括浮點(diǎn)數(shù)和整數(shù),然而,有時(shí)候我們可能會(huì)遇到一些意外的情況,比如將一個(gè)包含NaN(Not?a?Number)的浮點(diǎn)數(shù)轉(zhuǎn)換為整數(shù)時(shí),就會(huì)拋出錯(cuò)誤,本文將探討這個(gè)錯(cuò)誤的原因,并給出幾種可能的解決方案,需要的朋友可以參考下2024-09-09
Python實(shí)現(xiàn)約瑟夫環(huán)問題的方法
這篇文章主要介紹了Python實(shí)現(xiàn)約瑟夫環(huán)問題的方法,詳細(xì)分析了約瑟夫環(huán)問題的描述、原理與解決方法,需要的朋友可以參考下2016-05-05
DRF之請(qǐng)求與響應(yīng)的實(shí)現(xiàn)
本文主要介紹了DRF請(qǐng)求與響應(yīng)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-07-07
Python自動(dòng)化測(cè)試框架之unittest使用詳解
unittest是Python自動(dòng)化測(cè)試框架之一,提供了一系列測(cè)試工具和接口,支持單元測(cè)試、功能測(cè)試、集成測(cè)試等多種測(cè)試類型。unittest使用面向?qū)ο蟮乃枷雽?shí)現(xiàn)測(cè)試用例的編寫和管理,可以方便地?cái)U(kuò)展和定制測(cè)試框架,支持多種測(cè)試結(jié)果輸出格式2023-04-04
Python執(zhí)行命令并保存輸出到文件的實(shí)現(xiàn)方法
文章介紹了在Python腳本中執(zhí)行系統(tǒng)命令并將輸出內(nèi)容保存到文件的多種方法,主要對(duì)比了os.system()、os.popen()和subprocess.run()、subprocess.Popen()等四種方法的實(shí)現(xiàn)原理、適用場(chǎng)景和具體代碼示例,還提供了最佳實(shí)踐,需要的朋友可以參考下2026-04-04
Python-OpenCV深度學(xué)習(xí)入門示例詳解
深度學(xué)習(xí)已經(jīng)成為機(jī)器學(xué)習(xí)中最受歡迎和發(fā)展最快的領(lǐng)域。深度學(xué)習(xí)的常見應(yīng)用包括語音識(shí)別、圖像識(shí)別、自然語言處理、推薦系統(tǒng)等等。本文將通過一些示例代碼,帶你詳細(xì)了解深入學(xué)習(xí)2021-12-12

