日韩精品一区二区三区在线视频放-无码中文字幕V?一区二区-成年片免费观看视频-国内少妇人妻丰满av-国产精品中文字幕免费观看-亚洲成人久久一区二区三区-国内少妇偷人精品视频无缓冲-一区二区国产精品日本一区二区三区在线网

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運(yùn)營(yíng)的一線實(shí)戰(zhàn)洞察。

Flask多線程大文件上傳與實(shí)時(shí)進(jìn)度條實(shí)現(xiàn)指南

Flask多線程大文件上傳與實(shí)時(shí)進(jìn)度條實(shí)現(xiàn)指南 做文件上傳功能很多人第一反應(yīng)是“這有什么難的form里放個(gè)input后端request.files接一下就行”。但真到實(shí)際項(xiàng)目里尤其是要傳大文件、要展示實(shí)時(shí)進(jìn)度、要支持多用戶并發(fā)上傳的時(shí)候事情就完全不是那么回事了。我這次用Flask做了一個(gè)帶多線程處理和實(shí)時(shí)進(jìn)度展示的文件上傳頁(yè)面前后踩了不少坑把整個(gè)實(shí)現(xiàn)思路和關(guān)鍵代碼整理出來(lái)希望能幫到正在做類似需求的人。先講一下這個(gè)功能是干什么的用戶通過(guò)瀏覽器選擇一個(gè)或多個(gè)文件點(diǎn)擊上傳后前端頁(yè)面能實(shí)時(shí)顯示每個(gè)文件的上傳百分比和處理狀態(tài)后端接收到文件后不會(huì)只做簡(jiǎn)單的保存而是會(huì)丟到一個(gè)多線程任務(wù)里做后續(xù)處理比如解析、轉(zhuǎn)碼、壓縮、入庫(kù)等處理進(jìn)度同樣會(huì)反饋到前端。整個(gè)過(guò)程中頁(yè)面不需要刷新用戶能直觀看到“上傳中→處理中→完成”的完整鏈路。這套方案適合誰(shuí)參考如果你是剛接觸Flask的開發(fā)者或者正在做內(nèi)部工具、數(shù)據(jù)平臺(tái)、自動(dòng)化運(yùn)維系統(tǒng)這一類需要文件上傳并且要看得見進(jìn)度條的場(chǎng)景這篇內(nèi)容可以直接照著抄。我會(huì)把前端交互、后端多線程任務(wù)管理、進(jìn)度數(shù)據(jù)共享、接口設(shè)計(jì)全部拆開講包括我在實(shí)際開發(fā)中遇到的幾個(gè)典型問(wèn)題比如進(jìn)度丟失、線程風(fēng)暴、調(diào)試模式下reloader導(dǎo)致的詭異行為等。1. 整體設(shè)計(jì)與方案選型1.1 為什么不用現(xiàn)成的上傳組件而要自己實(shí)現(xiàn)市面上有不少現(xiàn)成的文件上傳組件比如Dropzone.js、webuploader、layui的upload模塊甚至Flask也有flask-uploads、flask-reuploaded這類擴(kuò)展。那為什么還要自己手寫一套原因有三點(diǎn)。第一現(xiàn)成組件大多只解決“上傳”這一步也就是把文件從瀏覽器送到服務(wù)器。但很多業(yè)務(wù)場(chǎng)景里真正的重頭戲是上傳之后的服務(wù)端處理比如圖片壓縮、Excel解析、視頻轉(zhuǎn)碼、數(shù)據(jù)清洗等這些組件的進(jìn)度條在文件落盤之后就結(jié)束了服務(wù)端處理階段完全是個(gè)黑盒。這個(gè)項(xiàng)目要的是“上傳處理”全流程的實(shí)時(shí)進(jìn)度這是現(xiàn)成組件給不了的。第二自定義實(shí)現(xiàn)意味著可以完全掌控交互細(xì)節(jié)。比如多文件同時(shí)上傳時(shí)每個(gè)文件要顯示獨(dú)立的進(jìn)度條某個(gè)文件處理失敗時(shí)要能單獨(dú)重試而不用重新傳整個(gè)文件處理過(guò)程中要允許用戶取消任務(wù)。這些需求在不同組件里的實(shí)現(xiàn)方式差別很大與其去讀別人的源碼做適配不如自己寫一套簡(jiǎn)單的反而更可控。第三從學(xué)習(xí)角度來(lái)說(shuō)自己實(shí)現(xiàn)一遍能讓你真正理解HTTP上傳機(jī)制、多線程協(xié)作、前后端通信這些基礎(chǔ)原理。這些東西搞明白了以后換任何框架都能快速上手。1.2 技術(shù)棧選擇Flask XHR 多線程 輪詢這套方案的核心技術(shù)選型并不復(fù)雜但每個(gè)選擇背后都有它的理由。后端用Flask這個(gè)沒(méi)什么好說(shuō)的輕量、靈活、上手快特別適合這種單機(jī)內(nèi)部工具類的應(yīng)用。如果項(xiàng)目規(guī)模大了可以換FastAPI但核心思想是完全一樣的。前端沒(méi)有用fetch而是用XMLHttpRequestXHR對(duì)象為什么因?yàn)閒etch雖然支持上傳文件但它的上傳進(jìn)度事件支持得不如XHR成熟。XHR有現(xiàn)成的xhr.upload.onprogress事件能拿到當(dāng)前上傳了多少字節(jié)做進(jìn)度條幾乎是零成本。fetch想要實(shí)現(xiàn)同樣的效果得自己用ReadableStream去包裝請(qǐng)求體復(fù)雜度一下子高很多。所以在這個(gè)場(chǎng)景下XHR反而是更務(wù)實(shí)的選擇。進(jìn)度展示這塊我用的是前端顯示“上傳進(jìn)度”瀏覽器→服務(wù)器后端用內(nèi)存字典保存“處理進(jìn)度”服務(wù)器內(nèi)部任務(wù)前端通過(guò)輪詢接口獲取處理進(jìn)度。為什么不直接用WebSocket或者SSE因?yàn)檫M(jìn)度信息的更新頻率并不高每秒鐘輪詢一次足夠了用WebSocket有點(diǎn)殺雞用牛刀的意味而且Flask原生要支持WebSocket還得額外裝Flask-SocketIO依賴變重了。多線程用來(lái)做什么兩個(gè)地方。第一后端接完文件后把耗時(shí)的處理任務(wù)丟到后臺(tái)線程里執(zhí)行主請(qǐng)求線程立刻返回避免用戶一直等著HTTP響應(yīng)第二一個(gè)上傳請(qǐng)求進(jìn)來(lái)時(shí)Flask本身會(huì)開一個(gè)線程去處理這個(gè)請(qǐng)求天然支持并發(fā)上傳。所以這里的“多線程”其實(shí)是兩層含義一個(gè)是Flask的請(qǐng)求并發(fā)模型另一個(gè)是任務(wù)層的自定義多線程。2. 前端頁(yè)面與交互實(shí)現(xiàn)2.1 頁(yè)面結(jié)構(gòu)和基礎(chǔ)樣式前端頁(yè)面我先做了一個(gè)簡(jiǎn)潔的上傳區(qū)域包含一個(gè)拖拽區(qū)、一個(gè)文件選擇按鈕、一個(gè)文件列表。文件列表里的每一項(xiàng)都會(huì)顯示文件名、文件大小、當(dāng)前狀態(tài)等待上傳/上傳中/處理中/已完成/失敗、進(jìn)度條、以及一個(gè)取消按鈕。HTML部分核心代碼是這樣的div classupload-container div classupload-dropzone iddropZone input typefile idfileInput multiple p拖拽文件到這里或點(diǎn)擊選擇文件/p /div ul classupload-list iduploadList/ul /div這里的multiple屬性一定要記得加否則用戶一次只能選一個(gè)文件體驗(yàn)會(huì)比較差。dropZone是整個(gè)可拖拽區(qū)域點(diǎn)擊它時(shí)觸發(fā)隱藏的fileInput的click事件實(shí)現(xiàn)“點(diǎn)擊選擇拖拽上傳”兩種方式。文件列表項(xiàng)是動(dòng)態(tài)生成的每個(gè)文件對(duì)應(yīng)一個(gè)li元素結(jié)構(gòu)如下li>const dropZone document.getElementById(dropZone); const fileInput document.getElementById(fileInput); dropZone.addEventListener(dragover, (e) { e.preventDefault(); dropZone.classList.add(dragging); }); dropZone.addEventListener(dragleave, () { dropZone.classList.remove(dragging); }); dropZone.addEventListener(drop, (e) { e.preventDefault(); dropZone.classList.remove(dragging); const files e.dataTransfer.files; for (const file of files) { uploadFile(file); } }); dropZone.addEventListener(click, () { fileInput.click(); }); fileInput.addEventListener(change, () { const files fileInput.files; for (const file of files) { uploadFile(file); } fileInput.value ; });注意最后一行fileInput.value 如果不重置input的值用戶連續(xù)選擇同一個(gè)文件時(shí)change事件不會(huì)觸發(fā)這個(gè)坑我踩過(guò)一次。2.3 XHR上傳與進(jìn)度監(jiān)聽核心的上傳函數(shù)這樣寫function uploadFile(file) { const taskId generateTaskId(); const listItem createListItem(taskId, file); const formData new FormData(); formData.append(file, file); formData.append(task_id, taskId); const xhr new XMLHttpRequest(); xhr.open(POST, /upload, true); // 上傳進(jìn)度監(jiān)聽 xhr.upload.onprogress (e) { if (e.lengthComputable) { const percent Math.round((e.loaded / e.total) * 100); updateProgress(taskId, percent, upload); } }; xhr.onload () { if (xhr.status 200) { // 上傳完成開始輪詢處理進(jìn)度 startPolling(taskId); } else { updateStatus(taskId, failed, xhr.statusText); } }; xhr.onerror () { updateStatus(taskId, failed, 網(wǎng)絡(luò)錯(cuò)誤); }; xhr.upload.onload () { updateStatus(taskId, processing, 處理中); }; xhr.send(formData); }xhr.upload.onprogress事件里拿到的是上傳階段的數(shù)據(jù)量這個(gè)進(jìn)度是從瀏覽器到服務(wù)器的傳輸進(jìn)度跟后端的處理進(jìn)度是兩碼事。當(dāng)xhr.upload.onload觸發(fā)時(shí)說(shuō)明文件已經(jīng)完整到達(dá)服務(wù)器這時(shí)候就應(yīng)該把狀態(tài)切成“處理中”然后通過(guò)輪詢獲取后端處理進(jìn)度。這里要提一個(gè)細(xì)節(jié)FormData里除了文件本身我還附帶了一個(gè)task_id字段。這個(gè)task_id是前端生成的用來(lái)把后續(xù)的進(jìn)度查詢請(qǐng)求和當(dāng)前上傳的這一個(gè)文件關(guān)聯(lián)起來(lái)。你也可以讓后端在響應(yīng)里返回一個(gè)任務(wù)ID但那樣就得多等一個(gè)請(qǐng)求而且處理起來(lái)稍微麻煩一點(diǎn)。2.4 進(jìn)度輪詢機(jī)制上傳完成后前端要定時(shí)去問(wèn)后端“這個(gè)任務(wù)處理到百分之多少了”這就是輪詢。我用setInterval實(shí)現(xiàn)每500毫秒查詢一次function startPolling(taskId) { const pollTimer setInterval(async () { const response await fetch(/progress/${taskId}); const data await response.json(); if (data.status done) { clearInterval(pollTimer); updateStatus(taskId, done, 已完成); updateProgress(taskId, 100, process); showSuccessMessage(taskId, data.result); } else if (data.status failed) { clearInterval(pollTimer); updateStatus(taskId, failed, data.error || 處理失敗); } else { const percent data.progress || 0; updateProgress(taskId, percent, process); updateStatus(taskId, processing, 處理中 ${percent}%); } }, 500); }輪詢邏輯不復(fù)雜但有幾個(gè)點(diǎn)要注意第一輪詢的終止條件要明確。狀態(tài)變成done或failed時(shí)必須clearInterval否則會(huì)一直請(qǐng)求下去浪費(fèi)資源。第二輪詢的狀態(tài)機(jī)要和前端的展示狀態(tài)保持一致。文件上傳周期里的狀態(tài)包括等待上傳、上傳中、處理中、已完成、失敗每一個(gè)狀態(tài)都要有對(duì)應(yīng)的UI表現(xiàn)不要出現(xiàn)“上傳完了狀態(tài)還卡在上傳中”的尷尬情況。第三輪詢間隔不要設(shè)得太短。500毫秒已經(jīng)算是比較激進(jìn)的頻率了如果服務(wù)器壓力大建議拉長(zhǎng)到1秒。實(shí)時(shí)進(jìn)度這玩意兒肉眼能區(qū)分的變化閾值大概也就是每秒幾個(gè)百分點(diǎn)太頻繁的輪詢反而加重服務(wù)器負(fù)擔(dān)。3. 后端核心多線程任務(wù)處理3.1 Flask接收文件與基礎(chǔ)保存后端最基礎(chǔ)的接口是接收上傳文件。這個(gè)邏輯本身不難import os import uuid import threading from flask import Flask, request, jsonify, render_template app Flask(__name__) app.config[MAX_CONTENT_LENGTH] 500 * 1024 * 1024 # 500MB UPLOAD_FOLDER os.path.join(os.path.dirname(__file__), uploads) os.makedirs(UPLOAD_FOLDER, exist_okTrue) # 全局進(jìn)度字典保存所有任務(wù)的進(jìn)度信息 # 結(jié)構(gòu)task_id - {progress: int, status: str, error: str, result: dict} TASKS {} TASKS_LOCK threading.Lock() app.route(/) def index(): return render_template(index.html) app.route(/upload, methods[POST]) def upload(): if file not in request.files: return jsonify({error: 沒(méi)有文件}), 400 file request.files[file] task_id request.form.get(task_id, str(uuid.uuid4())) original_filename file.filename if original_filename : return jsonify({error: 文件名為空}), 400 # 安全處理文件名防止路徑穿越 safe_filename os.path.basename(original_filename) # 給文件加一個(gè)唯一前綴避免重名覆蓋 saved_filename f{task_id}_{safe_filename} file_path os.path.join(UPLOAD_FOLDER, saved_filename) # 先保存文件 file.save(file_path) # 注冊(cè)任務(wù)進(jìn)度 with TASKS_LOCK: TASKS[task_id] { progress: 0, status: processing, error: , result: {}, filename: safe_filename } # 啟動(dòng)后臺(tái)線程處理文件 thread threading.Thread( targetprocess_file, args(task_id, file_path, safe_filename), daemonTrue ) thread.start() return jsonify({task_id: task_id, message: 上傳成功開始處理})這里有幾個(gè)安全細(xì)節(jié)值得強(qiáng)調(diào)。文件名必須用os.path.basename處理否則用戶上傳一個(gè)路徑為../../etc/passwd的文件名直接拼接路徑就可能造成文件覆蓋或路徑穿越問(wèn)題。這是文件上傳里最常見也最嚴(yán)重的安全隱患之一。MAX_CONTENT_LENGTH限制了請(qǐng)求體大小超過(guò)這個(gè)限制Flask會(huì)直接報(bào)413錯(cuò)誤。這個(gè)限制可以根據(jù)業(yè)務(wù)需要調(diào)整但不建議取消。3.2 任務(wù)處理函數(shù)與多線程模型process_file是真正的業(yè)務(wù)處理函數(shù)模擬了一個(gè)耗時(shí)的處理流程。實(shí)際項(xiàng)目中這個(gè)函數(shù)可能是解析Excel、生成縮略圖、壓縮視頻等等。我用一個(gè)簡(jiǎn)單的循環(huán)來(lái)模擬方便展示進(jìn)度更新的模式def process_file(task_id, file_path, original_filename): 后臺(tái)處理文件模擬一個(gè)耗時(shí)的操作并實(shí)時(shí)更新進(jìn)度 try: # 模擬處理步驟 steps [ (讀取文件內(nèi)容, 20), (數(shù)據(jù)解析與校驗(yàn), 40), (業(yè)務(wù)邏輯處理, 70), (生成結(jié)果文件, 90), ] for step_name, target_progress in steps: # 模擬每一步的子過(guò)程 time.sleep(1.5) update_task_progress(task_id, target_progress) # 完成 with TASKS_LOCK: TASKS[task_id][progress] 100 TASKS[task_id][status] done TASKS[task_id][result] { output_file: fresult_{task_id}.csv, rows: 1024 } except Exception as e: with TASKS_LOCK: TASKS[task_id][status] failed TASKS[task_id][error] str(e)理解了流程之后應(yīng)該能get到這套多線程模型的核心思路Flask請(qǐng)求線程負(fù)責(zé)接收文件并立即返回自定義的worker線程負(fù)責(zé)耗時(shí)處理并周期性地更新一個(gè)共享字典。這樣用戶不用守著瀏覽器等一個(gè)超長(zhǎng)的HTTP響應(yīng)。為什么不用Flask的stream_with_context或生成器來(lái)做這個(gè)事因?yàn)槟莻€(gè)是服務(wù)端主動(dòng)推流還是要保持HTTP連接在多用戶場(chǎng)景下連接數(shù)很容易打滿。后臺(tái)線程輪詢的方式雖然看起來(lái)“土”但勝在簡(jiǎn)單可靠服務(wù)器資源占用也小。3.3 共享進(jìn)度字典的線程安全設(shè)計(jì)多線程操作共享數(shù)據(jù)最核心的問(wèn)題就是“競(jìng)態(tài)條件”。比如兩個(gè)線程同時(shí)讀取并修改TASKS[task_id][progress]就有可能出現(xiàn)數(shù)據(jù)不一致的情況。Python里保證線程安全最常用的方式就是threading.Lock。在上面的代碼里所有對(duì)TASKS字典的寫操作都通過(guò)with TASKS_LOCK:包裹了起來(lái)確保同一時(shí)間只有一個(gè)線程能修改共享數(shù)據(jù)。為什么用鎖而不是用queue.Queue因?yàn)檫M(jìn)度查詢接口需要“隨機(jī)讀取”任意任務(wù)ID的當(dāng)前進(jìn)度用隊(duì)列的話很難實(shí)現(xiàn)這種隨機(jī)訪問(wèn)。字典天然支持按鍵查找配合鎖做同步剛好滿足需求。鎖這個(gè)東西用的時(shí)候要注意粒度。鎖的范圍越小越好只包裹真正需要同步的操作不要在鎖里面做耗時(shí)的I/O操作否則會(huì)阻塞其他線程。在上面的代碼里文件處理本身的耗時(shí)邏輯time.sleep和業(yè)務(wù)處理都在鎖外面只有更新進(jìn)度那一下才進(jìn)鎖這個(gè)設(shè)計(jì)是合理的。3.4 進(jìn)度查詢接口進(jìn)度查詢接口很簡(jiǎn)單就是查字典然后返回JSONapp.route(/progress/task_id) def get_progress(task_id): with TASKS_LOCK: task TASKS.get(task_id) if task is None: return jsonify({status: notfound, error: 任務(wù)不存在}), 404 return jsonify({ status: task[status], progress: task[progress], error: task[error], result: task[result] })這個(gè)接口就是給前端輪詢用的。返回的JSON結(jié)構(gòu)固定前端拿到之后就能更新UI。關(guān)于進(jìn)度字典的清理我單獨(dú)說(shuō)一下。如果不做清理TASKS字典會(huì)無(wú)限膨脹跑幾天就積累幾千上萬(wàn)條記錄內(nèi)存遲早吃不消。解決方案是定期清理已完成或失敗的任務(wù)。我用了一個(gè)簡(jiǎn)單的方式在查詢接口里如果任務(wù)狀態(tài)是done或failed且創(chuàng)建時(shí)間超過(guò)一定閾值就順手刪掉。更系統(tǒng)的做法是單獨(dú)起一個(gè)定時(shí)清理線程或者用帶過(guò)期時(shí)間的緩存比如cachetools的TTLCache。這個(gè)根據(jù)自己的需求來(lái)。4. 調(diào)試模式下的神秘問(wèn)題4.1 為什么進(jìn)度條會(huì)“抽風(fēng)”我在開發(fā)過(guò)程中遇到過(guò)幾個(gè)特別詭異的現(xiàn)象進(jìn)度條跳到一半突然跳回0或者后臺(tái)線程明明執(zhí)行完了但進(jìn)度死活停在90%再或者一個(gè)文件上傳后出現(xiàn)了兩條進(jìn)度記錄。排查了很久才發(fā)現(xiàn)根因是Flask的調(diào)試模式。Flask在debugTrue時(shí)Werkzeug服務(wù)器會(huì)自動(dòng)啟動(dòng)一個(gè)reloader來(lái)監(jiān)聽代碼變化代碼一變就自動(dòng)重啟服務(wù)。關(guān)鍵問(wèn)題在于reloader會(huì)啟動(dòng)兩個(gè)進(jìn)程一個(gè)是監(jiān)控進(jìn)程一個(gè)是實(shí)際運(yùn)行的應(yīng)用進(jìn)程。如果你直接運(yùn)行app.py代碼里的threading.Thread會(huì)在兩個(gè)進(jìn)程里都啟動(dòng)一遍然后兩個(gè)進(jìn)程各自維護(hù)一份TASKS字典互不共享進(jìn)度數(shù)據(jù)自然就亂了。更麻煩的是你的請(qǐng)求可能被負(fù)載均衡到兩個(gè)進(jìn)程里的任何一個(gè)處理導(dǎo)致上傳請(qǐng)求進(jìn)的是進(jìn)程A進(jìn)度查詢被進(jìn)程B處理了查詢結(jié)果永遠(yuǎn)是“任務(wù)不存在”。4.2 解決方案最直接的解決方案日常開發(fā)調(diào)試時(shí)用debugFalse。如果你確實(shí)需要自動(dòng)重載功能可以用use_reloaderFalseapp.run(debugTrue, use_reloaderFalse)這樣既能保留debug模式的錯(cuò)誤頁(yè)面和交互式調(diào)試器又不會(huì)啟動(dòng)雙進(jìn)程。不過(guò)我還是建議開發(fā)階段就使用debugFalse加手動(dòng)重啟的方式避免產(chǎn)生依賴reloader的壞習(xí)慣也省得哪天忘記關(guān)閉導(dǎo)致生產(chǎn)環(huán)境出問(wèn)題。如果項(xiàng)目真的部署到了生產(chǎn)環(huán)境千萬(wàn)不要用Flask自帶的開發(fā)服務(wù)器用gunicorn或uWSGI這類正式的WSGI服務(wù)器它們對(duì)多進(jìn)程、多線程的管理要成熟得多。只不過(guò)生產(chǎn)環(huán)境的進(jìn)度共享方案就得升級(jí)了因?yàn)槎噙M(jìn)程之間內(nèi)存不共享TASKS字典得換成Redis之類的外部存儲(chǔ)才行。5. 完整代碼與實(shí)操調(diào)優(yōu)5.1 后端完整代碼把上面的幾個(gè)片段拼起來(lái)完整的后端代碼如下import os import time import uuid import threading from flask import Flask, request, jsonify, render_template app Flask(__name__) app.config[MAX_CONTENT_LENGTH] 500 * 1024 * 1024 UPLOAD_FOLDER os.path.join(os.path.dirname(__file__), uploads) os.makedirs(UPLOAD_FOLDER, exist_okTrue) TASKS {} TASKS_LOCK threading.Lock() def update_task_progress(task_id, progress): with TASKS_LOCK: TASKS[task_id][progress] progress def process_file(task_id, file_path, original_filename): try: steps [ (讀取文件內(nèi)容, 20), (數(shù)據(jù)解析與校驗(yàn), 40), (業(yè)務(wù)邏輯處理, 70), (生成結(jié)果文件, 90), ] for step_name, target_progress in steps: time.sleep(1.5) update_task_progress(task_id, target_progress) with TASKS_LOCK: TASKS[task_id][progress] 100 TASKS[task_id][status] done TASKS[task_id][result] { output_file: fresult_{task_id}.csv, rows: 1024 } except Exception as e: with TASKS_LOCK: TASKS[task_id][status] failed TASKS[task_id][error] str(e) app.route(/) def index(): return render_template(index.html) app.route(/upload, methods[POST]) def upload(): if file not in request.files: return jsonify({error: 沒(méi)有文件}), 400 file request.files[file] task_id request.form.get(task_id, str(uuid.uuid4())) original_filename file.filename if original_filename : return jsonify({error: 文件名為空}), 400 safe_filename os.path.basename(original_filename) saved_filename f{task_id}_{safe_filename} file_path os.path.join(UPLOAD_FOLDER, saved_filename) file.save(file_path) with TASKS_LOCK: TASKS[task_id] { progress: 0, status: processing, error: , result: {}, filename: safe_filename } thread threading.Thread( targetprocess_file, args(task_id, file_path, safe_filename), daemonTrue ) thread.start() return jsonify({task_id: task_id, message: 上傳成功開始處理}) app.route(/progress/task_id) def get_progress(task_id): with TASKS_LOCK: task TASKS.get(task_id) if task is None: return jsonify({status: notfound, error: 任務(wù)不存在}), 404 return jsonify({ status: task[status], progress: task[progress], error: task[error], result: task[result] }) if __name__ __main__: app.run(debugTrue, use_reloaderFalse, host0.0.0.0, port5000)5.2 前端完整代碼前端把HTML、CSS、JavaScript放在templates目錄下的index.html里。我這里只貼核心JS部分樣式和網(wǎng)頁(yè)骨架可以根據(jù)自己的審美來(lái)let taskCounter 0; function generateTaskId() { taskCounter; return task_ Date.now() _ taskCounter; } function formatSize(size) { if (size 1024) return size B; if (size 1024 * 1024) return (size / 1024).toFixed(1) KB; if (size 1024 * 1024 * 1024) return (size / (1024 * 1024)).toFixed(1) MB; return (size / (1024 * 1024 * 1024)).toFixed(1) GB; } function createListItem(taskId, file) { const list document.getElementById(uploadList); const li document.createElement(li); li.dataset.taskId taskId; li.innerHTML div classfile-info span classfile-name${escapeHtml(file.name)}/span span classfile-size${formatSize(file.size)}/span /div div classprogress-wrapper div classprogress-bar/div /div span classfile-status等待上傳/span button classbtn-cancel取消/button ; list.appendChild(li); const cancelBtn li.querySelector(.btn-cancel); cancelBtn.addEventListener(click, () { // 取消邏輯可以根據(jù)需要實(shí)現(xiàn) li.remove(); }); return li; } function escapeHtml(str) { const div document.createElement(div); div.appendChild(document.createTextNode(str)); return div.innerHTML; } function updateProgress(taskId, percent, type) { const li document.querySelector(li[data-task-id${taskId}]); if (!li) return; const bar li.querySelector(.progress-bar); bar.style.width percent %; } function updateStatus(taskId, status, text) { const li document.querySelector(li[data-task-id${taskId}]); if (!li) return; const statusEl li.querySelector(.file-status); statusEl.textContent text; li.className status; }前端有個(gè)小細(xì)節(jié)要提一下escapeHtml函數(shù)很重要。用戶上傳的文件名可能包含script之類的HTML標(biāo)簽如果直接拼接進(jìn)innerHTML就存在XSS注入的風(fēng)險(xiǎn)。這個(gè)風(fēng)險(xiǎn)在文件上傳場(chǎng)景里特別容易被忽視。用escapeHtml先把文件名里的HTML字符轉(zhuǎn)義掉再拼進(jìn)DOM就安全了。5.3 啟動(dòng)與測(cè)試啟動(dòng)服務(wù)python app.py然后打開瀏覽器訪問(wèn)http://localhost:5000選擇一個(gè)大文件上傳最好選一個(gè)幾百M(fèi)B的文件這樣上傳進(jìn)度條變化比較明顯觀察上傳完成后的處理階段進(jìn)度是否符合預(yù)期。我這里用一個(gè)小技巧來(lái)方便測(cè)試開一個(gè)幾百M(fèi)B的虛擬文件# Linux / macOS dd if/dev/zero oftest_file.bin bs1M count200 # WindowsPowerShell fsutil file createnew test_file.bin 2097152006. 常見問(wèn)題與排查技巧6.1 上傳大文件超時(shí)現(xiàn)象上傳一個(gè)很大的文件請(qǐng)求在傳輸中途斷開或者頁(yè)面一直轉(zhuǎn)圈最終報(bào)錯(cuò)??赡茉蚝徒鉀Q辦法第一Nginx或反向代理的client_max_body_size限制不夠大默認(rèn)一般是1MB。需要在Nginx配置里加上client_max_body_size 500m;。第二Flask的MAX_CONTENT_LENGTH設(shè)置得不夠大請(qǐng)求體超過(guò)限制時(shí)被拒。這個(gè)在上面已經(jīng)說(shuō)過(guò)了。第三請(qǐng)求超時(shí)時(shí)間太短。如果是通過(guò)Nginx代理的proxy_read_timeout和proxy_send_timeout也要相應(yīng)調(diào)大。6.2 前端進(jìn)度條亂跳現(xiàn)象進(jìn)度條已經(jīng)到90%突然跳回10%然后再慢慢爬到100%。原因大概率是開啟了Flask的debug模式reloader雙進(jìn)程導(dǎo)致請(qǐng)求被打到不同的進(jìn)程上兩個(gè)進(jìn)程各自維護(hù)了一份進(jìn)度數(shù)據(jù)。最簡(jiǎn)單有效的解決辦法就是開發(fā)時(shí)用use_reloaderFalse生產(chǎn)環(huán)境用gunicorn或uWSGI并且把共享進(jìn)度存儲(chǔ)替換成Redis。6.3 Thread線程里拋異常進(jìn)度卡住不動(dòng)現(xiàn)象后臺(tái)線程在處理文件時(shí)報(bào)錯(cuò)了但前端進(jìn)度條永遠(yuǎn)停在最后更新的那個(gè)百分比狀態(tài)也不是failed。原因后臺(tái)線程的process_file函數(shù)里沒(méi)有做異常捕獲線程直接崩潰退出TASKS[task_id][status]一直停留在processing。解決辦法process_file函數(shù)一定要在最外層包一個(gè)try...except在except里把任務(wù)狀態(tài)更新為failed并記錄錯(cuò)誤信息。我上面的代碼已經(jīng)做了這個(gè)處理但實(shí)際項(xiàng)目里還是要根據(jù)業(yè)務(wù)需求細(xì)化異常分支。6.4 多線程并發(fā)更新進(jìn)度數(shù)據(jù)錯(cuò)亂現(xiàn)象多個(gè)文件同時(shí)上傳A文件的進(jìn)度條偶爾會(huì)顯示B文件的數(shù)據(jù)。原因所有線程共享一個(gè)TASKS字典如果更新操作沒(méi)有加鎖保護(hù)兩個(gè)線程同時(shí)寫同一個(gè)鍵值時(shí)就可能互相覆蓋。解決辦法對(duì)TASKS字典的所有讀寫操作加鎖。如果并發(fā)量非常大鎖的競(jìng)爭(zhēng)會(huì)很激烈這時(shí)候可以把進(jìn)度字典按task_id分片用多個(gè)鎖減少?zèng)_突。6.5 上傳文件保存路徑不對(duì)現(xiàn)象文件上傳成功后后端找不到保存的文件或者文件保存在了一個(gè)莫名其妙的位置。原因相對(duì)路徑問(wèn)題。開發(fā)時(shí)用相對(duì)路徑uploads/但當(dāng)前工作目錄可能不是你項(xiàng)目所在的目錄尤其當(dāng)你在IDE里或通過(guò)其他工具啟動(dòng)項(xiàng)目時(shí)工作目錄往往是設(shè)置文件所在目錄而不是項(xiàng)目根目錄。解決辦法路徑用絕對(duì)路徑基于__file__來(lái)拼接UPLOAD_FOLDER os.path.join(os.path.dirname(os.path.abspath(__file__)), uploads)6.6 前端XHR CORS跨域問(wèn)題現(xiàn)象前端頁(yè)面在一個(gè)域名后端接口在另一個(gè)域名上傳時(shí)瀏覽器報(bào)CORS錯(cuò)誤。解決辦法開發(fā)時(shí)用Flask-CORS擴(kuò)展或者手動(dòng)加響應(yīng)頭app.after_request def add_cors_headers(response): response.headers[Access-Control-Allow-Origin] * response.headers[Access-Control-Allow-Headers] Content-Type response.headers[Access-Control-Allow-Methods] POST, GET, OPTIONS return response生產(chǎn)環(huán)境更推薦用Nginx做同域反向代理從根上規(guī)避CORS問(wèn)題。6.7 上傳過(guò)程中文件數(shù)過(guò)多導(dǎo)致列表性能問(wèn)題現(xiàn)象一次上傳幾十上百個(gè)文件DOM里插入大量li元素頁(yè)面滾動(dòng)和操作明顯卡頓。解決辦法如果單次上傳的文件數(shù)很多建議做分頁(yè)展示或者虛擬滾動(dòng)只渲染當(dāng)前可視區(qū)域內(nèi)的文件項(xiàng)。如果上傳量不大用原生DOM操作已經(jīng)夠用不必上框架。7. 實(shí)際體驗(yàn)與進(jìn)一步優(yōu)化方向這套方案我現(xiàn)在已經(jīng)用在幾個(gè)內(nèi)部工具上了整體穩(wěn)定性不錯(cuò)日常使用沒(méi)有出過(guò)什么幺蛾子。但如果你想讓它在更嚴(yán)苛的環(huán)境下跑還有幾個(gè)方向可以擴(kuò)展。支持?jǐn)帱c(diǎn)續(xù)傳。當(dāng)前方案里上傳中斷后就得重新傳整個(gè)文件對(duì)大文件來(lái)說(shuō)成本太高。斷點(diǎn)續(xù)傳的標(biāo)準(zhǔn)做法是前端先把文件切片然后分片上傳后端記錄每個(gè)分片的上傳狀態(tài)最后合并。這會(huì)讓代碼量翻倍但體驗(yàn)提升也是明顯的。用Redis替換內(nèi)存字典做進(jìn)度共享。當(dāng)部署方式從單進(jìn)程變成多進(jìn)程或多機(jī)負(fù)載均衡后內(nèi)存字典的共享方案就失效了。用Redis的話各進(jìn)程都能讀寫同一個(gè)進(jìn)度存儲(chǔ)還能天然地處理過(guò)期清理算是一步到位的方案。支持取消任務(wù)。目前取消按鈕只是把前端的列表項(xiàng)刪了后端線程還在后臺(tái)跑著。真要實(shí)現(xiàn)取消需要一個(gè)threading.Event之類的信號(hào)機(jī)制讓后臺(tái)線程在處理過(guò)程中定期檢查這個(gè)信號(hào)收到取消通知就提前退出。線程本身不能被強(qiáng)制kill只能靠協(xié)作式取消。增加文件類型校驗(yàn)和病毒掃描。文件上傳是安全重災(zāi)區(qū)建議在保存文件前做MIME類型校驗(yàn)、擴(kuò)展名白名單校驗(yàn)再結(jié)合內(nèi)容檢測(cè)工具做進(jìn)一步掃描。這部分對(duì)內(nèi)部工具可能不是必需但只要是面向外部用戶的服務(wù)一定要重視。最后說(shuō)一個(gè)我個(gè)人的小體會(huì)文件上傳這功能看著簡(jiǎn)單一旦跟多線程、進(jìn)度反饋、并發(fā)這些詞沾上邊復(fù)雜度就指數(shù)上升了。好在這套“前端XHR 后端線程 內(nèi)存字典 輪詢”的組合幾乎覆蓋了所有內(nèi)部工具類場(chǎng)景代碼又少又直觀出問(wèn)題也好排查。如果你正在做類似的功能直接照這個(gè)思路來(lái)就行后面根據(jù)自己的業(yè)務(wù)往里填處理邏輯會(huì)比從零開始捋清思路快很多。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
日韩精品熟妇| 99999国产| 久久风骚城市人| 日韩在线欧美精品一区二区| 人妻AV 中文字幕的| 大香蕉99re| 91碰超| av中文字幕在线熟女| 校园春色 亚洲| 亚洲精品一二区| 超碰 另类 欧美| 看免费的黄片| 99精品久久久久久| 亚欧Av| 91美女国产在线| 色狠狠 - 百度| 60秒免费小视频| 免费精品国偷自产在线在线| 亚洲欧洲第二视频在线观看色图| 无码外流操逼视频| 亚洲黄a三级三级三级看三级| 国产人妖的免费的视频| 蜜臀无码一区二区| 一区二区三区机械有限公司| 97资源站国产精品| 淮穴色AV| 久久婷婷电影网| 蜜桃臀 后入 一区 二区 三区 在线| 91人妻少妇| 国产专区路线| 加勒比在线视频一区二区三区| 亚洲视频小说| 欧美三级中文字幕hd| 99日视频在线免费| 欧美色偷拍 | 夜夜操夜夜爽夜夜高潮| 日韩福利综合一区| 天综合网欧美| 欧美日不卡| 久热一区二区| 免费亚洲国产精品久久一区| 大香蕉综合在线| 欧美96在线|欧| 亚洲天堂色图| 91精品丝袜在线观看| 成人三级片一区二区三区视频| 日本在线一二 | 日韩精品人妻中文字幕不卡乱码| 男人的天堂啪啪| 97超碰超碰| 久久精品老司| 激情五月婷婷| 免费黄色片。| 91人妻人人澡人人爽人人精品| 97人妻色| 91亚洲人电影| 久久一级无码精品毛片6| 日韩精品一区二区人人人| 丰满少妇人妻久久久久久| 强奸乱伦 亚洲一区| 青青草玖玖爱| 色女综合| 狠狠综合| 91狼人| 99婷婷一区二区| 91少妇通奸网站| 亚洲精品蜜桃久久久一区二区三区| 亚洲一区二区三区播放在线| 久久国产乱子伦精品免费女,网站| 人妻社区男人天堂| 亚洲性综合9| 精品偷拍13p欧美dodk视频| 加勒比综合88| 青草伊人网| 美女毛片999| 亚洲天堂电影精品一区| 骚逼高潮久久精品| 超碰人妻在线| 粉嫩粉嫩一区性色AV片| 男女性感激情网站| 国产丝袜美女在线一区| 中文视频在线观看| 亚洲欧美97| 韩国毛片一区二区三区| 欧美天堂超碰97| 国产女上位好爽在线| 日本在线激情一区二区三区 | 国产99999久久精品| 久操大香蕉手机视频在线看 | 2021国产成人精品久久| 九九九九AV| 天天影视之亚洲综合网| 98福利在线视频| 日韩av熟女一区二区三区成人| 丁香六月婷婷综合| 97av在线视频| 色综合色色| 国产浮力影院第1页| 欧美gv在线观看| 91精品91久久久久77777俄罗斯老妇姓x| 91青青在线视频| 热热色色综合| 亚洲自拍欧美色综合| 91最新综合| 一区=区三区视频| 大香蕉在线视频15| 色九月综合| 大香蕉免| 亚洲精品 超碰| 97视频观看| 欧美一区二区在线资源| 久久99精品九九久久久婷婷| 久久久久元码视频| 国产性爱欧美性爱在线| 中文字幕一区二区三区蜜桃视频| 日韩精品午夜操呦呦不卡影院| 久操| 天堂成人网| 欧美色青| 岛国1区2区3区在线观看| 亚洲成人激情小说视频| 欧美日产国产在线成人第一区| 熟妇xxxxx性春色| 91视频伊人| 超碰97人妻免费在线| 一起草高清无码| 无遮挡男女激烈动态图| 丰满岳乱妇一区二区三区| 天天干天天燥| 最新日韩黄片| 男人下部插入女人下部| 欧美成人综合| 国产免费内射视频| 性爱乱伦视频免费| 丁香成人五月天| 欧插网站| 偷拍2020| 国产亚洲精品一区二区三区| 亚欧成人中文字幕一区| 久久欧洲| 人伦四五区| 四虎在线视频| AA级电影三区| 啪啪综合网| 毛片视频白嫩| 插入粉嫩少妇视频| 资源新线在线天堂| 2019天天干天天操| 熟妇精品juliaannAV| 国产日产欧产美韩系列麻豆免费| 亚洲欧美另类激情小说| 97欧美性爱| 久久久久人妻| 97碰碰日本乱偷人妻中文的| 2020中文字幕在线观看| 国产精品一级特黄aaa大片在线观看| 欧美在线观看综合国产| 97天天综合网| 999精品乱码| 国产亚洲精品久久久久小| 性爱精品一区| aaa淫乱视频| 揉揉揉夜夜| 高清在线不卡一区二区 视频| 亚洲情色中文字幕一区| 91网亚洲| 国产午夜精品在线观看| 日日夜夜狠狠| 中文熟女五十乱码在线| 色就色综合| 啊啊啊啊嗯嗯嗯用力好爽 | 一本久久精品中文字| 青青操在线视频| 亚洲精品国产熟女| 熟妇高潮一区二区免费视频| 五月天精品| 九九综合久久中文字幕| 久草久热| 亚洲天堂一区| 素人播放一区| 欧美日韩人妻精品系列一区二区三区| 人人射人人操人人摸| 久久婷婷在线观看视频| 97欧美精品| 桃花色涩综合影院| 无码人妻丰满熟妇区毛片| 五月天我淫我色av| 综精品久久久aaaa| 国产精品福利资源在线尤物| 欧美综合站| 99福利社| 一区二区三区精品久久| 天天操天天7| 亚洲精品国产无码高清| 神马午夜久久久| av激情亚洲五月天| 天堂日本亚洲欧美| 国产日韩欧美操逼视频| 久操大香蕉超碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰碰 | 熟妇色99| 久久久噜噜噜久久久| 酒色综合网| 亚洲成人免费在线| 久久线上视频免费看| 在线国产探花| 无码人妻一区二区三区四区老鸭窝| 999亚洲国产视频| 91丨精品丨国产丨丝袜| 屁屁影院一区二区三区国产 | 黄色污污污污污污网站| 欧美大片91| 黑丝内射一区二区三区| 91亚洲人| av大香蕉网站| 亚欧免费| 丝袜视频网国产90| 婷婷久久综合| 久久的免费性爱视频| 日韩欧美亚洲国产日韩| 欧美黄色手机在线观看| 中 文字幕一区二区三四 五 区日 日 骚| 操操吧亚洲乱伦视频| 啊啊啊快操我视频| 国产成人无码网站在线视频| 男人精品区| 无码人妻一区二区一牛影视| 蜜臀AV秘一区翔田千里| 性交一区二区在线播放| 亚洲欧美中文日韩视频中国语 | 久久超碰、| 无遮挡一级毛片视频免费的| 超碰97欧美日韩| 黄片不用下载在线观看| 日韩精品-原创伙伴| 天天射网| 天天干天天操天天干天天操| 夜色91| 桃色六月天| av网站在线观看了| 九九九精品美女| 久久伊人在线五区| 国产一区二区视频在线播放| 亚洲国产综合视频| 欧亚日韩综合精品国产| 视频二区美腿丝袜制服人妻欧美| 伊人一区二区在线播放| 国产精品另类一区大香蕉| 日韩专区数据列表-第3230页-精品国产一区二区三区香蕉 久久99熟女人妻中文字 | 色色丁香| 密乳无码| 日日超碰亚洲| 99热在线只有精品| 91成人在线| 久久xxxx| 国产高清26uuu| 欧美人妻制服| 老熟乱一区二区三区四区| 欧美狠狠弄| 在线看免费无码AV天堂的| 日本一级真人黄色性爱视频| 久久久久96| 亚洲 se图 欧美电影| 精品人人插人人操| 影音先锋视频在线| 亚洲国产成人精品久久久国产成人一区二区| 好看的久久不射无码影视影院| www色色com| 精品中文字幕第一页| 91狠狠综合久久| 97精品视频免费| 91九色精品熟女内射| 亚洲图片日本AⅤ欧美在线| 亚洲 在线| 四虎影视永久在线观看精品免费网站| 亚洲少妇诱惑| 97干色天堂| 深爱五月婷婷| 免費黃色視頻觀看一| 一个国产在线综合网站| 亚洲精品国产精品成人| 91成人无码| 蜜桃午夜视频一区二区 | 国产60区。| 天美国产三级传媒| 国产黄a三级三级三级av在线看| 日韩AV无码中文一区二区| 国产在线综合网| 91人妻熟女| 97久久超碰亚洲| 日本熟妇色熟妇在线视频播放| 激情图片伦理国产一区二区日韩| 国产成人亚洲精品自产在线| 午夜免费视频1000| 日韩人妻大香蕉| 另类天堂| 韩日精品四区| 欧美中文字幕男人天堂久久精品 | 久久久久婷婷| 成人精品在线观看| 骚逼高潮久久精品| 91久精品| 欧美色66| 91丝袜激情在线| 色色99| 欧美激情 日韩精品| 日本一二区免费| 偷拍欧美激情| 久久999久| 久偷拍| 久久精品免视看国产成人﹣蜜臀av一区. 久久精品免视看国产成人,蜜臀av一区 | 精品成人动漫一区二区| 亚洲乱码国产乱码精网站| 亚洲午夜福利在线影院| 国产AV久久久蜜爱影集| 香蕉综合网| 日本韩国一本产品小视频日本韩国一本产品久久久产品小视频日本韩国一本产品久 | 欧洲精品一级二级精品综合视频综合 | 久操黄色视频| 欧美情色贴图| 精品对白久久不卡| 91精品无码久久久久久久| 最新av中文字幕高清| 黑人白女精品一区| 久久久国产成人一区二区三区在线| 狠狠综合| 久久久不卡区一区二区三区久久久| 久久內射| 九九九不卡| 亚洲日韩电影| 一级@啪啪视频| 久草视频在线视频在线视频在线观看| 国产精品青草综合久久| 国产精品小视频一区二区三区| 男人的天堂.com| 插老姨肥穴| 九99久久| 日本色色色网站免费看不卡| 91社区拍啪人妻| 99久久久| 天天摸夜夜操视频| 2017亚洲天堂| 91人妻素女| 黄污污污污| 亚洲美女色图| 午夜精品久久久久久久久久久久久| 襙一襙| 探花精品视频| 日本九九久久99| 97chaopengongkai| 久久一级无码精品毛片6| 亚洲宅男天堂| 九九这里只有精品| 国产日韩欧美操逼视频| 在线黄页看毛片| 精品中文日韩字幕视频| 91天天| 国产精品香蕉热久久新品| 青青欧美在线| 中文字幕黄色一起草| 激情五月综合网| 久草综合网| 综合网久久| 69久久| 国内精品a| 亚洲高潮少妇| 91亚.色| 青草草免费网站av| 久久黄黄| 欧亚 另类 久| 日韩成人电影AV| 欧美999999| 久久婷婷电影网| 亚洲AV成人无码一区二区三区在线观看| 超碰亚洲欧美日韩无| 激情视频图片| 日本欧美亚洲高清在线看| 亚洲AO在线| 麻豆91熟妇人妻中文字幕茄子| 国产精品久久99日日| 精品人妻一区二区三区-国产| 97chaopenrihan| 秋霞一级A片黄色视频| 久久久久成人蜜桃精品| 综合五月天| av片在线观看免费播放| 国产AB视频| 亚洲色 国产 欧美 日韩| 91色综合激情| 亚洲啪AⅤ永久无码| 毛片视频白嫩| 久久性爱视频99| 色鬼在线综合| 手机看片日韩人妻| 操死我了嗯嗯嗯| 午夜操逼不卡| 1024午夜激情男人的天堂| 中文日本免费高清| 日韩精品影视| 性一交一乱一交A片久久四色| 日本五区不卡| 91丨九色丨东北熟女| 欧美亚洲丝袜人妻制服中文99| 十八禁啪啦拍视频无遮挡| 91精品国| 天美传媒av 在线| 午夜精品久久99蜜桃的功能章节| 大香蕉 222| 亚洲啪AⅤ永久无码| 10000部十八禁看电影| 在线欧美69V免费观看视频| 99re在线视频| 日本色婷婷| 日韩兔费看黄片| 中文字幕制服欧美久久一区| 午夜无码精品免费看性色| 97久久精品不卡| 99亚洲天堂| 国语av狠狠色丁香婷婷综合激情| 色大香蕉97N| 国产亚州高清国产拍精| 久久 精品| 亚洲欧美爆| 精品高清一区二区三区三州| 亚洲天天天| 91N欧美| 久久久久久亚洲Av无码| 欧美精品欧美精品系列| 麻豆黄色五月天| 亚洲制服aⅴ中文字幕| 91天天爽| 国产中文福利| 亚洲激情四射| 涩涩涩综合| 精品久操| 欧美精品双插| 激情看片网站| 中文字幕日韩人妻视频| 啊啊啊 在线| 日本天天吊| 操操操日本的逼| 99re黄| 国产欧美日韩臀| 欧美性生活综合| 999久久久免费精品国产牛牛| 97在线观视频免费观看| 欧美亚洲综合色| 久久社区一区二区三区| 91夜色| 午夜操一视频一区| 日韩不卡码| 久久草草欧美精品| 亚洲精品无码少妇久久| 翔田千里无码中出中文字幕| 麻豆成人影音在线| 91在线一起| 淫骚熟女一区二区三区| 91久久精品蜜臀| 欧美亚洲日本视频久久久| 69XX一中文字幕人妻91| 亚洲人妻久久| 亚洲女优有码无码高清| 任你干在线视频| 国产男女无套97| 成人五月香网在线| 大香蕉AV在线| 99久久精品国产高潮| 小日子操bb在线看| 欧美一级美片在线观看免费| 日本精品五区| 欧美色图电影| 国产一二三福利视频网| 尤物视频新赏网鲜网色诱网| 综合一区二区影视| 风月影院男女十八禁| 97爱b| 久久久久9999妇女| 97露脸精品丝袜| 久久久久久性爱视频| 理论久久婷婷网 8| 激情开心五月天| 欧美色五月| 欧美成人性爱视频大全| 91精品无码人妻系列| 国产一级不卡在线观看| 天天色黄色影院天天操| 久久久久久久久久va| 97综合| 91亚洲欧洲| 青青青青青手机视频| 免费男人的天堂| 欧美日日人人天天| 少妇熟女视频一区二区三区| 超碰在线综合97| 色五月激情AV在线| 亚洲极品| 国产嫩草精品A88AV| 九九久久国产精品怡红院| 无卡一区=区| 91中出在线| 91精品人妻一区二区三区蜜臀| 精品人妻久久久| 91宗合网| 欧美操人视频| 国产精品久久久久亚洲av| 日韩丰满熟妇| 六月色婷婷| 亚洲综合春色| 蜜桃色院一区久久 | 美国aaaaa一级黄片| 在线v中文字幕一区二区三区| 天天躁日日躁XXXXYY| 三级三久久线久久99久目本WW| 色综合色欲色综合色综合色综合| 久久精品人体| 91美女看B| 偷拍亚洲情色| 国产在线观看一区二区三区| 亚洲国产尤物yw在线观看| 欧美系列在线一区二区| 国产欧美伊人| 欧美亚洲宗合色性图| 欧美瑟综合| 日韩少妇无码| 久久久久久69国产一区二区| 天天操天天舔| 国产少妇与亚洲av| 国产精品电影推荐| 91天天综合日韩欧美| 国产区日韩区在线观看| 国产三级中文有码在线视频| 六月婷婷激情| 久久精品免视看国产成人﹣蜜臀av一区. 久久精品免视看国产成人,蜜臀av一区 | 国产熟女自拍| 69麻豆天美| 黄色电影在线播放综合网站| 美女自卫慰黄网站免费| 日本一二区免费| 亚洲高清男人天堂| 男人的天堂网页| 美中日韩无码| 无码欧美有限公司| 色哟哟精品1精品2| 午夜啪| 久久草大香蕉| 激情四射五月天| 亚洲欧美第一页| 亚洲成人性爱在线观看| 久久精品一区| 国产女人视频三四五区| 精品区国产区一区二区三区| 91国产美女丝袜足交精品视频| 成人国产二区三区在线,男女精品。| wwwxxx日本爽| 九九九久千久久激情蜜桃在线看| 婷婷人妻激情| 国产黄色av大片网站| 欧美se亚洲| 丰满人妻区一区二区三| 99精品网| 久久久涩| 国内毛片无码一级毛片| 91精品免费| 天天摸夜夜添无码小视频| 黄色大片一区二区密桃丝袜| 亚洲天堂加勒比| 九九热AV| 99999久久精| 噜噜噜无码AV一级一级久久影院| 98超碰日本| 免费一级黄色录像影片| 国产精品91ai| 97在线精品观看视频| 欧美久久毛片基地| 18禁看网站一区| 蜜臀亚洲中文| 成人av性爱电影在线观看| 国内精品999| 人妻91少妇| 乱伦熟女区| 色欧洲97| 老司机射| 91老熟女91老女人| 青青操狠狠撩| 色欧美色交综合| 亚欧性爱ab| 久久综合久色欧美综合狠狠| 黄色AAAAA欧美| 91夜夜蜜桃臀1区2区3区| 欧美综合网| 欧美亚洲激情小说| 自拍内地三级在线观看| 久久嫩草国产成人一区| 一区二区三区免费视频入口| 亚洲不卡av在线| 欧美岛国精品在线观看| 九热大香蕉| 7777奇米影视久久| 97伪v| 黑丝自慰喷水网站| 久久久少妇诱惑精品视频| 色香色欲天天综合网天天来吧| 久久久三区二区一区| 97色在线| 91视频伊人| 国产情色在线| 九月丁香婷婷| 91久久免费视频互動交流| 久久日本熟女精品一区| 国产成人无码久久精品| 天天综合欧美| 天天看高清麻豆| 国产精品99久久久www| 天天影视亚洲| 美美91成人国产精品欧美精品久久久久久久 | 91老熟女91老女人| 精品国产乱码久久久久久久久1| 日本国产亚洲一区在线观看| 色爽——AV| 五月天激情小说| 国产福利视频精品视频| 日本二区不卡| 日日干夜夜操视频h| 在线无码操| 九九九热| 啊啊啊啊,啊啊好多水 | 久久精品国产72国产精品福利| 一区二区影视| 91 亚洲情侣偷拍 久久| 曰韩精品九九无码| av无码精品久久久久| 亚洲丝袜综合| 污电影在线观看| 狼人综合婷婷激情四射 | 日韩乱伦影音先锋| 加勒比中文av| 操淫穴亚洲五月丁香 | 国产免费一区二区在线A片视频| av网站免费看| 熟妇女伦乱视频视频| 东北黄色电影| 日本肉体xxxx裸交| 男人女人18禁片免费看网站| 天天综合站| 婷婷三区| 丁香五月激情啪啪| 香蕉久久AⅤ...| 射综合网| 久久久96精品| 欧美页片| 久久黄色网址| 欧美十八禁在线看| 国产精品亚洲一级av第二区| 91人妻少妇| 亚洲 中文 欧美 日韩 在线| 国产丰满熟夫69mpp| 亚洲日本韩国极品一区二区| 日韩成人网址| 大伊香蕉在线视频免费| 久久久久免费看少妇A片特黄| 亚洲精品九九九| 97久久超碰日韩精品| 四虎免费视频| 99re在线视频| 久久久成人精品| 亚洲诱惑| 久久爱超碰网| 夜草欧美| 亚洲情色在线| www.高清无码诱惑一区.com| 情色五月天久久久| 少妇与黑人高潮在线| 精品一区二区三区最新| 国产欧美第五页| 亚洲激情网一二三四区| 99综合网| 九色 人妻 大香蕉| 婷婷色一区| 欧美日不卡| 97人妻色| 日韩伦理视频| 日本午夜福利视频| 久操 高清| 丰满少妇高潮无码| 9长久久精品| 亚洲精品不卡一二三区| 午夜精品久久久久久久| 懂色av中文字幕| 在线观看啊啊啊啊啊| 久久中文字幕人妻熟av女蜜柚| 91麻豆天美国产欧美高潮| 亚洲日本大香蕉1| 天天操天天干美女网址导航| 欧美色图20P| 东京热双插| 国产又黄又粗又猛大片| 日韩极品无码B| 两性色网| 91久久久久久| 人妻密肉在线观看| 日韩美女高潮喷水视频| 久久久熟女一区| 成人无码欧美一级A片狼牙直播| 久午视频| av爱爱爱| aaaa黄片| 亚洲精品819| 激情终合网| 一摸二插三插| 亚洲另类春色| xxxx网站亚洲精品| 91精品黄在线观看| 国内91熟女人妻丝袜天天精品视频在线| 精品久久久久,69国产成人精| 夜夜高潮夜夜爽高清视频一 | 区日韩亚洲乱码av电影| 欧美色图成人网一区二区 | 好爽要喷了| 一起草视频在线| 无码外流操逼视频| 中文字幕在线高清男人的天堂 | 人人干黄色| 成人AV素股で擦久久| 午夜爽爽爽在线观看永久入口姬片| 五月丁香| 九九九不卡| 蜜臀99久久国产| 91新在线欧美| 欧美色图亚洲色,麻豆| 1区2区3区视频| www.99中文字幕| 婷婷五月天激情网| 中文字幕超碰CAO| 91亚洲综合在线| 夜夜天天噜狠狠爱2021| 欧美亚洲丝袜人妻制服99| 神马久久69| 中欧人妻丝袜中文字幕| 日本性爱不卡视频| 啊啊啊啊啊操我视频| 嗯嗯嗯啊啊在线观看| 夜夜操美女| 五月婷婷六月色| 中文字幕文字幕无码一区二区三区电影99 | 一级黄色性爱裸体视频| 99精品久久久久久久婷婷蜜桃| 日韩av熟女一区二区三区成人| 疯操AV| 韩国一级婬片A片无码天美| 大香樵伊人网| 五月天九九日国产精品一区二区三区| 青娱乐福利99| 久久,精品一二三| 97伦乱| 久久久久久69国产一区二区| 98一区二区精品| 精品大全99999| 天天操天天射天天日| 97超碰色五月| 青青青在线高清视频在线一二三四区 | 330dv亚洲成年视频网| 精品国产Av无码久久久伦古装| 国产激情av女片自拍| 九九热这里只有在线精品视 伊人草 成人菠萝蜜视频在线观看 | 91超级碰碰| 欧美女同在线| 精品一区二区人妖| 色臀aV| 高清成年美女黄网站免费大全 | 色播五月丁香| 天美传媒精品久久视频| 欧美精品久久久久久久久88| 日噜夜夜夜夜夜夜夜夜夜夜爽爽爽爽爽爽爽爽爽爽爽爽 | 2025亚洲男人天堂| 天天澡天天狠天天天做| 亚洲国产精品久久久久久久久久| 中文字幕免费在线观看| α√在线| 天天草AV| 欧美性爱视频免费一区一A| 好屌色综合| 操逼免费视频无码国产| 亚洲情色在线| 久久人人爽爽人人爽人人片αV| 亚洲大色堂| 91精品微拍福利| 国产午夜视频| 国产成人欧美一区二区三区的国产| 欧洲亚洲人妻无码中字久久三区四区| 粉嫩av平台| 亚洲熟妇乱女区二区三区| 久久人妻| 欧美中文字幕一区 | 老熟乱一区二区三区四区| 日本三级韩三级99久久| 亚洲高清色综合| 91在线页| av一区二区三区不卡| 禁十八久久| 淫荡熟女乱伦网| 91熟女少妇| 99久久婷婷国产综合精品草原| 亚洲a色| 麻豆 欧美 日韩| 国产精品久久天天干| 欧洲与亚洲欧美精品中文字幕| 亚洲成人免费中文字幕| 欧美日韩国产色五月综合在线| 精品国产片亚洲一区| 另类欧美色| 欧美内射少妇| 久久精精区一区二区一蜜桃一区二区| 精品欧美乱码久| 用力操死我| 国产女人和拘做爰视频| 在线观看啊啊啊啊啊| 色噜噜综合在线| 亚洲精品国产日韩无码AV永久免| A一级色女| 色99在线| 天天狠操| 98超碰欧美| 久久成年精品| 婷婷操逼| 中文字幕人成乱码熟女香港| 久久久久深夜无码| 国产欧美一区二区| 亚洲综合中文字幕有码| 视频不卡中文字幕| 国产丸一视频| 91啪9色| 熟女字幕| 操逼操2| 中文字幕无码不卡啪啪| 97超碰无码网| 国产免费一区在线观看| 日韩字幕一区| 五月天婷婷社区| 男生女生啊啊啊啊| 五月婷婷激情| 操高情无码| 国产精品不卡高清在线观看| 成人一级二级| 色老汉色| 正在播放:深夜激情大战,自带黑丝袜全力输出骚穴 | 嗯嗯啊啊视频在线看| 成人精品一区二区91毛片不卡| 不卡视频一区蜜桃视频 | 99www.bibizy香蕉资源国产一区二区三区高清| 日本韩欧美在线播放a| 精品久久久久久AV无码| 一本色道久久综合精品婷婷| 亚州高清色综合| 国产精品 亚洲情色| 91色久| 乱论91| 最新av中文字幕高清| 亚洲图片第一页| 超碰 欧美| 青青草狠狠撸| 大香蕉综合久久| 欧美大香蕉卡久久| 思思热在线视频精品| 顶级丝袜熟女一区二区三区| 波多野42部激情无码喷潮| 啊啊啊爽爽| 清纯唯美综合| 玖色av| 96精品一区| 激情四射五月天| 欧美色图成人网一区二区 | 超碰99在线| 91精品国产高清久久久久久,亚洲成人 | 热久日综合| 欧美日韩黄色片一区二区三区四区人与兽做爱| 狠狠综合网| 99久久九九| 五月天激情影院| 超碰人人超在线观看| 美国aaaaa一级黄片| 先锋色眉乱伦资源| 四虎影视永久在线免费| 另类小说综合网| 黄视频免费| 国产又色又粗又黄又爽| www.99色| 国产传媒午夜理伦精品| 日婷婷| 又大又长又粗又爽又黄| 国产欧美日韩一区二区三区| 日本成人A片网站| 亚洲国产欧美一区二区潘金莲| 亚洲精品免费中文字幕| 草莓精品视频在线免费观看| 国产操逼视频在线观看| 亚洲性爱免费电影| 家庭乱伦性爱av| 人人妻人人狠人人| 久久婷婷热| 亚洲人妻一区二区三区| 国产精品91一样| 欧美色图亚洲特色| 中文字幕十五区| 中文字幕伊人| 91日韩网站| 四虎精品一区二区| 无码久久亚洲高清,| 日本黄色大片一级视频免费麻豆| 午夜福利在线合集| 国产男女无套视频免费观看| 亚洲精品视频在线| 肥臀熟女福利视频一区二区| 中文在线久久字幕| 中文子幕一二三| 啪啪自拍九九综合| AV麻豆免费一区| 亚欧无码在线| 久久99久久99久久99人受| 欧美制服另类丝袜| 四季AV一区二区凹凸精品小说| 色香91| 人人操人人肉久久精品| 黑人精品一区二区在线播放| 日本成人电影资源网| 99免费在线视频| 爱欲AV| 97欧美色| 99婷婷一区二区| 黄片aaaaa一区| 超碰人人妻| 久久99精品国产| 久久精品国产亚洲粉嫩| 久久亚洲国产成人| 日本国产成人亚洲精品无码| 久久久久久久久久久97| 91女优在线观看| 99国产精品| 18禁的网站在线| 伊人综合色网| 人妻天天爽夜夜爽2| 天天插网| 国产强奸超碰AV| 91亚洲不卡一区| 一本大道久| 一个色导综合| 97人人草| 校园春色亚洲无码| 91日韩| 国产做?爰片久久毛片?片美国| 密臀在线免费观看| 殴美色网| 国产精品麻豆视频网站| 97久久资源| 亚洲天堂少妇| 国产操逼视频在线观看| 亚洲 欧美 91| 人妻喷水| 色色色欧美| 伊人97色天使| 亚欧洲日韩国产精品| 国产一区二区三区导航| 久热影视| 你懂得91| 九九九偷拍| 国产18精品亚洲精品| 国产日韩怡红院| 亚洲欧美校园| 天天爽天天爽| 美女91| 操香逼| 五月丁香六月激情综合| 超碰美女97| 亚洲欧美爆| 黄色网址在线免费观看| 伦伦成年午夜免费视频| 东北丰满熟女国产一区| 思思热国产在线视频| 久久直播国产| 久久久 国产精品| 97人人干人人操| 国产亚洲一黄| 久久九九网| 看全色黄大色大片免费视频| 精品久久久不卡一区二区| 欧美系列在线一区二区| 日韩欧美成人大香蕉| 免费的黄片有限公司| 日本淫穴在线| 欧美一区二区三区日韩| 大白逼三四级| 99re在线视频| 亚洲欧美伦综合| 中日韩久久久| 成功精品影院| 校园春色中文字幕AV| 超碰97丝袜| 国产亚洲精品美女久久久久久2021| 亚洲乱熟女一区二区三区大香蕉| 亚洲一区二区麻豆影院| 日本A级视频| 加勒比无码一区二区三区| 欧美91在线+|+欧美| 992大香蕉| 免费观看国产不卡av| 中文字幕丰满子伦无码专区在线视频最新| 亚洲欧美日韩国产丝袜自拍中文| 日韩精彩免费| 99re6在线视频播放免费精品| 天天综合麻豆视频| 91动漫操逼视频| 美女AV一区二区| 第四色奇米影视777| 白丝jkav| 国产成年免费大片黄在线观看| 91成人社区| 国产第25页在线观看| 亚洲欧美变态| 天天干美少妇一区| 久久的免费性爱视频| 99re只有精品| 啪啪资源网| 精品国产乱码久久久影院| 志村玲子视频一区二区| 翔田千里AV无码秘 三区| 青青草精品| 天天色综亚洲91污| 大香蕉伊人75| 抽查国产福利主播| 十八禁的黄污污免费网站| 天天综合,91入口| 偷拍欧美综合| 免费日韩黄片| 91热色| 人妻天堂综合网| 人妻干天天| 久久久三区二区一区| 天天看综合网| 欧美一级久久久久久久大片动画 | 天天欲望网| 精品99999久久久久久| 岛国激情视频软件| 天天综合网~91| 亚洲色图 欧美热图 清纯唯美 另类自拍 | 蜜臀无码一区二区| 久久久久久亚洲精品不卡人乳| 91被操| 亚洲资源吧| 超碰99热中文字幕| 91粉嫩萝控精品福利网站_精品影音先锋国 | 国产丝袜一区二区三区| 天堂九九九九九九九九九| 巨爆乳一区二区爆乳区| 欧美成97爱| 一区二区三区无卡视频在线观看| 午夜男女爽爽爽在线视频| 成人小说另类在线| 婷婷人妻激情| 殴洲老熟女| 亚洲色图超碰在线| 黄色性爱网网| 日韩AV无码网站| 欧美日韩色| 欧美性天天影院| 91女在线观看| 日本道久久综合色色| 亚洲欧洲自拍图片专区满春格| 国产夫妻性生活视频| 欧美97在线观看| 加勒比大香蕉视频在线| 日韩A优精品在线观看| 在线免费观看高清无码视频| 久久宗合97| 97国产色图| Av色五月| 色婷婷五月天| 日韩AC| 伦伦成年午夜免费视频| 搡老熟女免费视频 | 激情情色五月天| 欧美久久婷婷| 男人的天堂2018东京热啪啪啪| 成人免费不卡在线视频| 日韩av免费一级电影| 亚洲欧美不卡线| 99久久久| 久久九九精品一区二区| 嫩草影院永久在线制服丝袜| 欧美激情亚洲色图| 六六久久日韩不卡| 婷婷丁香六月| 一区三区啪啪| 夜夜影视四色| 丰满人妻无码一区二区三区| 亚洲国产成人精品999| 亚洲色人| 91人妻熟女| 中文字幕视频二区| 久久九九久精品国产尤物|国产精品爽黄69天堂A片潘金莲,国产亚洲精品第一综合 | 久久偷拍人| 久九干| 乱人伦 国语对白:视频直接看| 午夜精品久久久久久久99蜜桃一| 久久久久久久久久久免费精品| 电家庭影院午夜69久久夜色精品国产69乱 | 熟女突然公开看18禁影片| 最新的亚洲无吗| 亚洲欧美在线观看免费| 国产毛片在线| 园内精品自拍视频在线播放| 6080YYY午夜理论片在线观看| 91露脸熟女专区| 日韩综合成人免费视频| 久久久久久99AV无码免费网站| 九九九九精品九九九九| 欧美亚洲图片| 天美91| 国产高清亚洲日韩一区| 国产精品午夜AV完会免费 | 国产一区二区精品在线视频| 日日噜噜夜夜久久亚洲一区二区 | 校园春色之综合网| 少妇蜜汁| 人妻一区久久二区三区色播| 黑人天8A∨高清网站| 亚洲精品天天影视综合网 | 欧美色图私拍91| 老熟妇综合| 中文一区在线日| 青青草一区二区三区四| 日本不卡中文| 干B网| 国产午夜精品在线观看| 色欲Av人妻精品一区二| 探花一区二区三| 国产女生在线| 五月天婷婷色| 色狠狠综合噜一二三区| 国产美女精品| 婷婷激情五月综合| 熟妇熟女一区二区三区| 日本爽爽爽爽爽爽免费视频| 亚洲精品国产日韩无码AV永久免 | 亚洲av青草久久一区二区| 日韩福利综合一区| 国产强上视频在线观看| 成人在线日韩| 国产精品久久久久绯色| 是还免费视频1727我| 亚洲图片 欧美电影| 欧美18老人禁| 国产人伦a片信息免费片| 伊人久久婷婷| 欧中日成人免费影视| 青青草久草AV| 精品午夜福利| 国产精品女生av| 五月色综合| 久草婷婷| 98超碰欧美| 最新亚洲人成网站在线影院| 丁香九月激情啪|