使用Python實(shí)現(xiàn)大文件切片上傳及斷點(diǎn)續(xù)傳的方法
概要
本文使用python實(shí)現(xiàn)大文件切片上傳,并支持?jǐn)帱c(diǎn)續(xù)傳,將功能劃分為:獲取上傳文件接口狀態(tài)、獲取臨時(shí)文件夾狀態(tài)信息、切片上傳、切片合并四個功能模塊。提供了詳細(xì)的思路和功能源碼。
整體架構(gòu)流程
步驟示例: 1.前端收到用戶的上傳的文件后,會執(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)行存儲文件上傳狀態(tài),key為"機(jī)器_id + 文件路徑(例如118/tmp/123.jpg)",value為自制一個
{ 'hash_code': 文件hash值, 'person_id': 用戶_id }
- 有上傳記錄:如果redis中存在正在上傳的記錄,則比對用戶id是否為同一個人,是則可以斷點(diǎn)續(xù)傳,否則阻止其他人上傳。
2.當(dāng)文件狀態(tài)通過后,前端會調(diào)用get_file_dir_info接口,并傳入主機(jī)_id、用戶_id、文件路徑(包括文件名)。 get_file_dir_info接口主要有三個功能:
- 檢查臨時(shí)文件夾:判斷用于存放切片的 “臨時(shí)文件夾” 是否存在。臨時(shí)文件夾的完整路徑由 “文件路徑 + : + 用戶 id” 拼接而成,例如 “/tmp/123.jgp:12”。若該文件夾不存在,系統(tǒng)會自動創(chuàng)建它。創(chuàng)建完成后,對臨時(shí)文件夾路徑進(jìn)行哈希計(jì)算,再借助 Redis 存儲相關(guān)信息,以哈希值作為 key,臨時(shí)文件夾的全路徑作為 value,方便后續(xù)執(zhí)行刪除文件夾操作時(shí)快速定位。
- 檢查切片上傳記錄臨時(shí)文件:查看 “臨時(shí)文件夾” 下的 “切片上傳記錄臨時(shí)文件” 是否已經(jīng)存在。如果文件存在,就讀取其中的內(nèi)容;要是不存在,就對其內(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ù)的文件處理流程提供支持。
最后遍歷整個臨時(shí)文件夾,獲得一個文件已上傳塊數(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.上傳完成后前端會再次調(diào)用get_file_dir_info接口,獲得當(dāng)前文件夾下已存在的文件塊數(shù)量與列表,與自己分塊進(jìn)行對比。如果成功則調(diào)用合并文件接口。 5.前端發(fā)起合并請求時(shí),會調(diào)用 bigfile_merge 接口,同時(shí)傳入用戶 id、文件塊數(shù)、文件路徑以及臨時(shí)文件夾路徑這些關(guān)鍵參數(shù)。 后端在接收到前端傳來的數(shù)據(jù)后,展開一系列校驗(yàn)與操作:
- 參數(shù)校驗(yàn):對傳入的路徑與文件塊數(shù)進(jìn)行準(zhǔn)確性核查,如果正確則開始合并。
- 合并方式選擇:合并功能主要分兩種執(zhí)行路徑。一是檢測目標(biāo)機(jī)器是否支持運(yùn)行 merge.py 腳本,若支持,便直接執(zhí)行已傳入的腳本完成合并;若不支持,則執(zhí)行目標(biāo)機(jī)器文件的操作方法來執(zhí)行合并。
- 斷點(diǎn)續(xù)合判斷:開始合并前,要先確認(rèn)需要合并的文件是否已經(jīng)存在。若文件已存在,意味著這是一個斷點(diǎn)文件,此時(shí)需讀取臨時(shí)記錄文件中的 size 和 chunk 值,從而判斷文件的實(shí)際大小以及已合并的塊位置。一旦發(fā)現(xiàn)實(shí)際文件大小超過記錄文件中的數(shù)值,就表明合并過程曾出現(xiàn)中斷,此時(shí)要把文件指針調(diào)整到記錄值對應(yīng)的位置,接著從該記錄值對應(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ù)解析出錯",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ī)出錯"}, 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)有相同的人傳同一份文件,但前一個人并沒有合并完成 #如果都沒有則創(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ī)器傳輸同一份文件,但也會把同一個人想斷點(diǎn)傳輸攔截 #所以在此key的value中保存帶有person_id的臨時(shí)文件夾路徑信息 #使用person_id將同一個人放行 retrieved_data = r.get(str(host_id)+full_path) # 將獲取到的數(shù)據(jù)轉(zhuǎn)換為字典對象 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': "該文件正在上傳,請勿多次操作"}, 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ī)出錯"}, 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 #初始化記錄文件對象 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 #上傳一個支持異步合并的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) #文件夾下有一個txt記錄文件,總文件塊數(shù)需要減一 fobject.num = len(file_list)-2 chunk_list =[] for file in file_list: parts = file.rsplit(":", 1) # 按冒號從右到左分割為最多兩個部分 if len(parts) == 2: last_part = parts[1] # 獲取最后一個部分 try: chunk_list.append(int(last_part)) except Exception as e: continue print("最后一個值:", last_part) else: print("未找到冒號,跳過") 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ù)塊信息 #第幾個數(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號文件塊開始傳輸",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í)文件的文件夾,在上一個獲取臨時(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("存儲臨時(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) #文件夾下有一個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)建或打開用戶的存儲文件 # ----------------開始合并操作------------------------------- # rb+ 追加覆蓋的方式打開用戶需要存儲文件,指針初始位置為文件開頭 remote_file = tmp_sftp._sftp.open(full_path,mode='rb+') #默認(rèn)當(dāng)前塊數(shù)從1開始 i=1 #創(chuàng)建文件記錄對象 fobject = FileObject(file_name,0,0) print('創(chuàng)建記錄對象成功') # 斷點(diǎn)續(xù)傳記錄文件 record_file = tmp_path+short_name+".txt" try : #如果記錄文件存在,則存儲文件大概率也存在 #如果兩個文件打開出錯 則走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í)記錄,一個是文件夾hashcode,用于驗(yàn)證與刪除目標(biāo)文件夾 #一個是host_id+路徑,用于標(biāo)記該路徑下這個文件正在傳輸 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é)
本文提供了一個python大文件上傳功能實(shí)現(xiàn)的思路,希望能夠你帶來一些思路和啟發(fā)。
以上就是使用Python實(shí)現(xiàn)大文件切片上傳及斷點(diǎn)續(xù)傳的方法的詳細(xì)內(nèi)容,更多關(guān)于Python大文件切片上傳及斷點(diǎn)續(xù)傳的資料請關(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)測試的實(shí)現(xiàn)
相關(guān)文章
django 發(fā)送手機(jī)驗(yàn)證碼的示例代碼
本篇文章主要介紹了django 發(fā)送手機(jī)驗(yàn)證碼的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-04-04python基礎(chǔ)之面對對象基礎(chǔ)類和對象的概念
這篇文章主要介紹了python面對對象基礎(chǔ)類和對象的概念,實(shí)例分析了Python中返回一個返回值與多個返回值的方法,需要的朋友可以參考下2021-10-10Python中OpenCV實(shí)現(xiàn)簡單車牌字符切割
本文將結(jié)合實(shí)例代碼,在Jupyter Notebook上使用Python+opencv實(shí)現(xiàn)如下簡單車牌字符切割。感興趣的小伙伴可以參考一下2021-06-06python 多線程實(shí)現(xiàn)檢測服務(wù)器在線情況
本文給大家分享的是Python使用多線程通過ping命令檢測服務(wù)器的在線狀況,給大家了內(nèi)網(wǎng)和外網(wǎng)的2個例子,有需要的小伙伴可以參考下。2015-11-11Python進(jìn)程,多進(jìn)程,獲取進(jìn)程id,給子進(jìn)程傳遞參數(shù)操作示例
這篇文章主要介紹了Python進(jìn)程,多進(jìn)程,獲取進(jìn)程id,給子進(jìn)程傳遞參數(shù)操作,結(jié)合實(shí)例形式分析了Python多進(jìn)程、父子進(jìn)程以及進(jìn)程參數(shù)傳遞相關(guān)操作技巧,需要的朋友可以參考下2019-10-10Python批量刪除或移動指定圖像的實(shí)現(xiàn)示例
本文主要介紹了Python批量刪除或移動指定圖像,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03python 實(shí)現(xiàn)在Excel末尾增加新行
下面小編就為大家分享一篇python 實(shí)現(xiàn)在Excel末尾增加新行,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-05-05python list數(shù)據(jù)等間隔抽取并新建list存儲的例子
今天小編就為大家分享一篇python list數(shù)據(jù)等間隔抽取并新建list存儲的例子,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11