欧美成人午夜精品久久久,国产?V天堂一区二区三区,欧美精品va在线观看,亚洲一区二区三区免费在线观看,av无码精品一区二区久久,欧美性爱视频不卡一区三区,欧美乱人伦视频在线观看,国产一级牲交高潮

ARTICLE DETAIL

資訊詳情

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

Vue3+Node.js大文件斷點續(xù)傳:文件分片、hash計算與并發(fā)上傳實踐

Vue3+Node.js大文件斷點續(xù)傳:文件分片、hash計算與并發(fā)上傳實踐 看到這個標題我想你大概是遇到了一個現(xiàn)實問題項目里要傳幾百MB甚至幾個G的文件結果每次傳到一半就斷要不就是頁面卡死、用戶關掉又重新從0%開始。這套基于JavaScript、Vue、Node.js寫出來的大文件斷點續(xù)傳DEMO就是為了解決這個痛點。我會用Vue 3配合File API里的slice方法做文件分片用spark-md5計算文件hash再用axios并發(fā)上傳分片最后寫一個不算復雜的Node.js服務端做分片存儲和合并。整個過程下來有Vue基礎的人照著敲一遍就能跑通也能理解斷點續(xù)傳背后的原理。1. 先把斷點續(xù)傳這件事想明白拆片、記錄、重組1.1 一次上傳為什么靠不住在開始寫DEMO之前先倒回去想一個問題為什么不能直接拿一個文件往后端丟不是不能而是現(xiàn)實世界里大文件直接傳輸的體驗非常差。瀏覽器里的File對象本質上是一個Blob你把一個幾個GB的文件塞進FormData一個HTTP請求發(fā)出后整個請求體一直占著網絡連接服務器那邊如果用的是Express這類框架默認也會對整個請求做緩沖內存一下就繃緊。更關鍵的是傳輸中間只要有一點抖動TCP連接斷了整個請求就沒了瀏覽器不會因為文件太大就對你手下留情用戶體驗就是“卡在99%然后重來”。還有一個容易被忽略的問題很多網關和Web服務器對單次請求體都有硬性限制Nginx默認的client_max_body_size很小就算你配置了十幾GB也架不住中間機器超時回收。所以“把一個文件變成很多個小文件分別上傳”是繞開這些限制的最實用手段這也是斷點續(xù)傳方案的核心一個文件被切成N片每片都是一個獨立請求失敗了對單一片重試而不是對整個文件重試。1.2 斷點續(xù)傳和普通上傳到底差在哪普通上傳里文件的二進制流從頭傳到尾中間任何一個地方斷掉已經傳出去的字節(jié)就全部作廢。斷點續(xù)傳則圍繞“狀態(tài)”來做文章先記住這個文件的唯一身份再記住哪些分片已經上傳成功下次重新打開頁面或者點擊續(xù)傳時只需要從沒傳過的分片繼續(xù)已經傳過的分片直接跳過。這個“記住”的過程就是后端只認hash和分片序號hash是文件指紋分片序號是位置。把斷點續(xù)傳和完整上傳放在一起對比差異就非常明顯傳輸粒度完整上傳是單一數據流斷點續(xù)傳是一個個獨立分片。失敗成本完整上傳一旦失敗全部重來斷點續(xù)傳只需要重傳失敗的那幾片理想狀態(tài)下接近零成本。可暫停性完整上傳不能中途暫停恢復斷點續(xù)傳隨時可以暫停下次繼續(xù)。秒傳能力如果服務端已經保存過同樣hash的文件可以直接提示上傳完成根本不用再上傳。這些差異是斷點續(xù)傳被選來對付大文件的核心原因。1.3 整體方案選型為什么選Vue 3 Node.js嚴格說起來斷點續(xù)傳是前端工程和前后端契約共同完成的不涉及什么黑魔法。demo里我用的組合是Vue 3 axios spark-md5后端用Node.js Express multer主要是因為這套組合和你標題里的“JavaScript怎么編寫”完全在一個語言體系里前端JavaScript負責切片、算hash、并發(fā)控制后端JavaScript負責收分片、存狀態(tài)、合并文件前后端不需要切換技術棧看起來更順。Vue 3的Composition API在管理上傳任務時比Vue 2的Options API直觀得多ref、computed這些響應式原語能把文件、hash、已上傳分片、進度這些狀態(tài)統(tǒng)一管理起來。demo里我不會引入太重量的UI庫直接用原生button和簡單的div滾動條因為斷點續(xù)傳的核心不在UI組件而在于分片、hash和狀態(tài)同步你以后接Element Plus還是Ant Design Vue都很容易。提示如果你在真實項目里用的是React、Angular或原生JS這套思路同樣成立因為真正核心的是File.slice、FormData和XHR/fetch這些瀏覽器標準API框架只是外層殼。2. 前端分片與指紋計算從代碼層面吃透2.1 用 Blob.slice 把大文件切成小塊文件分片最根本的API就是Blob.prototype.sliceFile繼承自Blob所以可以直接file.slice(start, end)拿到原文件的一部分。這個操作不是把文件物理切開而是生成一個新的Blob底層可能仍然引用原文件的內存區(qū)域所以性能很高不會因為切一下就把大文件整個復制一遍。以4MB為一片舉例分片邏輯如下const CHUNK_SIZE 4 * 1024 * 1024 function createFileChunks(file) { const chunks [] let start 0 while (start file.size) { const end Math.min(start CHUNK_SIZE, file.size) const chunk file.slice(start, end) chunks.push(chunk) start end } return chunks }這里有幾個細節(jié)要特別注意。第一最后一片的大小往往小于CHUNK_SIZE切片時end要用Math.min兜住文件大小否則會拿到一個空的Blob。第二每個分片必須帶上它在整個文件中的序號否則服務端合并時不知道先后順序這也就是后面接口里一直出現(xiàn)的index字段。第三切片時盡量讓每片大小保持一致這樣服務端合并、前端重試時邏輯都簡單。實際上分片大小不是一個固定的最佳值我見過很多人直接套一個“5MB”到處用。片越小請求數量越多服務端文件句柄和前端Promise對象都會壓滿片越大單請求傳輸時間越長斷點續(xù)傳的“續(xù)”就變得不精細。通常我建議從2MB到10MB之間取值公網環(huán)境取4MB左右比較平衡內網環(huán)境可以放大到10MB請求數量和恢復精度都兼顧了。2.2 用 spark-md5 算出文件唯一指紋分片之后遇到第一個問題怎么讓服務端知道“這兩個文件其實是同一個”最可靠的方案不是文件名因為同名文件內容可以完全不一樣也不是文件大小因為大小相同內容也可能不同而是給文件內容本身算一個摘要也就是hash。前端把整個文件的hash發(fā)給后端后端拿hash作為分片目錄名這樣同一個文件即使你改了文件名再傳也能直接續(xù)上。spark-md5是前端算文件hash最常用的庫它支持增量計算可以邊讀分片邊更新hash不用一口氣把整個文件讀進內存。核心代碼如下import SparkMD5 from spark-md5 async function calcFileHash(file) { const spark new SparkMD5.ArrayBuffer() let offset 0 while (offset file.size) { const chunk file.slice(offset, offset CHUNK_SIZE) const buffer await chunk.arrayBuffer() spark.append(buffer) offset CHUNK_SIZE } return spark.end() }這里值得說一下ArrayBuffer模式spark.append可以接收ArrayBuffer、binary string等對大文件來說用ArrayBuffer模式的性能更好因為瀏覽器底層可以直接給你二進制內存塊。每次循環(huán)只把當前這一片讀成buffer算完就釋放內存占用量被控制在一個分片大小內。如果文件達到了幾個GB這個計算過程可能要花幾十秒為了不阻塞界面真實項目最好放到Web Worker里跑demo階段為了少繞一圈直接放主線程后面我會專門講怎么處理卡UI的問題。有一點必須提前說清楚hash算出來的值只代表你本地看到的內容不代表傳輸過程中沒有損壞。真正要求嚴謹的場景服務端在合并完成后還要再算一次hash和前端提交的hash比對不一致就說明傳壞了。demo里我會在合并接口里做一次最簡單的數量校驗。2.3 分片上傳并發(fā)控制的實現(xiàn)思路分片準備好了hash也有了是不是直接把所有分片一次性全部發(fā)出去不行。幾百個分片同時發(fā)起請求瀏覽器連接數有限服務器也會被瞬間打掛進度條回升得又慢又不穩(wěn)定。正確做法是控制并發(fā)比如最多同時傳3到5片。并發(fā)控制寫起來一點都不難核心就是一個消費者隊列維護一個游標分給固定數量的worker去消費待上傳列表每個worker取到一片傳一片傳完繼續(xù)取下一下片直到全部消費完。我習慣寫成這樣async function uploadWithConcurrency(pendingList, poolLimit) { let cursor 0 const workers [] const runWorker async () { while (cursor pendingList.length) { const current cursor const item pendingList[current] await uploadChunk(item.chunk, item.index) } } const workerCount Math.min(poolLimit, pendingList.length) for (let i 0; i workerCount; i) { workers.push(runWorker()) } await Promise.all(workers) }這樣寫的好處是讓每個worker異步循環(huán)拉任務而不是先分配固定任務這樣慢的分片不會拖累其他worker。實際跑的時候你可以在uploadChunk里給每個分片附加index和hash并在成功后把index記錄進已上傳集合中。暫停功能的核心也是這個隊列暫停時用一個標志位讓worker的while循環(huán)直接退出并且取消掉正在進行的axios請求恢復時用當前已上傳集合過濾出還沒傳的分片重新建一個隊列跑起來。3. 服務端接口怎么設計才能支撐前端這套玩法3.1 約定三個核心接口check、upload、merge前端和后端的分工必須通過接口契約固定下來demo里我把它收成三個接口這也是大多數斷點續(xù)傳服務的最小集合接口方法核心參數職責/api/checkPOSThash查詢服務端已存在哪些分片序號/api/uploadPOSTfile、hash、index上傳單個分片/api/mergePOSThash、name、total通知服務端合并所有分片check接口的作用是“斷點查詢”。用戶打開頁面重新選擇一個文件前端算完hash后先問服務端這個文件傳過嗎傳到了第幾片服務端把已有分片的索引數組返回給前端前端過濾掉這些序號只傳剩下的。這個接口讓“續(xù)傳”真正成立不然每次重開頁面都不知道從哪里繼續(xù)。upload接口接收的就是某一個分片的二進制這里有個重要約定分片必須帶hash和index。hash決定它落到哪個目錄index決定它在合并時的位置。為了簡化設計后端按hash建目錄目錄里的文件名直接就是“index.part”查找已上傳分片就是讀取目錄下有哪些part文件。merge接口是最后一擊前端把所有分片都傳完后通知后端合并。這個接口不能少因為如果每傳一片就直接往最終文件尾部追加網絡亂序會讓你根本無法還原文件正確做法是先落盤成獨立分片最后統(tǒng)一排序合并。3.2 分片落盤與斷點記錄后端我用Express multer來接分片。multer是表單文件處理的標準庫配置磁盤存儲后文件會自動保存到指定目錄我們只需要在存儲配置里把req.body里的hash和index取出來拼路徑即可。const path require(path) const fs require(fs) const multer require(multer) const upload multer({ storage: multer.diskStorage({ destination(req, file, cb) { const dir path.join(UPLOAD_DIR, req.body.hash) fs.mkdirSync(dir, { recursive: true }) cb(null, dir) }, filename(req, file, cb) { cb(null, ${req.body.index}.part) } }) }) app.post(/api/upload, upload.single(file), (req, res) { res.json({ code: 0, message: ok }) })很多初次寫斷點續(xù)傳的人會在這里犯一個錯誤在destination回調里通過req.body拿到字段。如果你去打印發(fā)現(xiàn)req.body是空的原因通常是multipart字段順序不對。multer解析表單時文件字段之前的文本字段會先進入req.body所以前端構造FormData時最好把hash和index放在file字段之前append這個問題就自然消失了const formData new FormData() formData.append(hash, fileHash.value) // 先放文本字段 formData.append(index, index) formData.append(file, chunk) // 再放文件斷點記錄不需要引入數據庫因為文件系統(tǒng)本身就是記錄某個hash目錄下有多少個part文件已經自然記錄了上傳進度。check接口做的事情就是讀目錄app.post(/api/check, (req, res) { const { hash } req.body const dir path.join(UPLOAD_DIR, hash) const uploaded fs.existsSync(dir) ? fs.readdirSync(dir).map((name) Number(name.split(.)[0])) : [] res.json({ code: 0, uploaded }) })在真實場景里如果你的服務端是多機部署或者有幾十萬人同時傳文件文件系統(tǒng)就不是可靠的記錄方式了那時候得換Redis之類的記住分片狀態(tài)。demo階段用文件系統(tǒng)邏輯最透明也最容易排查問題。3.3 合并分片時的順序與校驗問題合并接口收到請求后要讀取hash目錄下所有part文件按序號從小到大依次寫入最終文件。順序錯了整個文件就是亂碼。寫合并邏輯時有兩點值得注意。第一sort不能直接按文件名字符串來因為10.part會排在2.part前面必須把文件名里的序號解析成數字再比較。第二大文件合并不要用readFileSync把所有分片同時讀進內存幾個GB的文件可能會把Node進程內存撐爆。正確姿勢是用流式管道或者用ReadableStream的異步迭代邊讀邊寫const { createReadStream, createWriteStream, existsSync, mkdirSync, readdirSync } require(fs) async function mergeChunks(hash, name) { const dir path.join(UPLOAD_DIR, hash) const chunkFiles readdirSync(dir) .filter((item) item.endsWith(.part)) .sort((a, b) Number(a.split(.)[0]) - Number(b.split(.)[0])) const outputPath path.join(MERGED_DIR, ${Date.now()}-${name}) if (!existsSync(MERGED_DIR)) mkdirSync(MERGED_DIR, { recursive: true }) const ws createWriteStream(outputPath) for (const file of chunkFiles) { const rs createReadStream(path.join(dir, file)) for await (const data of rs) { if (!ws.write(data)) { await new Promise((resolve) ws.once(drain, resolve)) } } } ws.end() await new Promise((resolve, reject) { ws.on(finish, resolve) ws.on(error, reject) }) }合并完成后強烈建議用絕對路徑讀寫文件避免文件名里帶上../或者/之類的路徑穿越問題。真實項目中文件名不能直接拿來拼路徑應該用hash作為存儲名或者做一層白名單過濾demo里用name主要是方便看到合并結果。注意不要在真實項目里讓用戶傳來的文件名直接落盤一定要對文件名做清洗或重命名。這是很多文件上傳服務被上傳惡意文件的入口。4. 完整DEMO實操從前端Vue到后端Node的落地過程4.1 初始化工程與依賴先建一個最簡單的工程。我特意不引入復雜的腳手架只保留能跑通斷點續(xù)傳的核心依賴方便你放進自己的項目里改造。mkdir vue-big-file-upload-demo cd vue-big-file-upload-demo npm init -y npm install express multer axios spark-md5 npm install -D vite vitejs/plugin-vue后端部分需要express和multer前端部分需要axios和spark-md5。vite只是demo的啟動工具你可以理解成幫我們把Vue單文件組件編譯到瀏覽器能跑的程度。目錄結構我習慣把前后端放在同一個工程里前端代碼放src后端代碼放servervue-big-file-upload-demo ├── src │ ├── App.vue │ └── main.js ├── server │ └── index.js ├── index.html ├── vite.config.js └── package.jsonvite.config.js里配置devServer代理把/api開頭的請求轉發(fā)到后端的3000端口import { defineConfig } from vite import vue from vitejs/plugin-vue export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { /api: http://localhost:3000 } } })main.js和index.html最簡單能掛載App.vue就行// src/main.js import { createApp } from vue import App from ./App.vue createApp(App).mount(#app)!-- index.html -- !DOCTYPE html html head title大文件斷點續(xù)傳DEMO/title /head body div idapp/div script typemodule src/src/main.js/script /body /html4.2 前端頁面與上傳邏輯App.vue是整個demo的前端核心。我用ref維護文件對象、hash、已上傳分片集合和進度再用一個上傳狀態(tài)字段標記當前處于“計算hash中、上傳中、已暫停、已完成”哪個階段。這三個狀態(tài)字段看著簡單但斷點續(xù)傳最怕的就是前端狀態(tài)沒管好。template div input typefile changehandleFileChange / button clickstartUpload上傳/button button clickpauseUpload暫停/button button clickresumeUpload續(xù)傳/button div文件指紋{{ fileHash || 等待計算 }}/div div總體進度{{ percent }}%/div div狀態(tài){{ status }}/div /div /template script setup import { ref } from vue import axios from axios import SparkMD5 from spark-md5 const CHUNK_SIZE 4 * 1024 * 1024 const file ref(null) const fileHash ref() const uploadedIndexes ref([]) const totalChunkCount ref(0) const percent ref(0) const status ref(idle) const pauseFlag ref(false) const cancelControllers ref([]) function handleFileChange(e) { file.value e.target.files[0] } function createChunks(sourceFile) { const chunks [] let start 0 while (start sourceFile.size) { const end Math.min(start CHUNK_SIZE, sourceFile.size) chunks.push(sourceFile.slice(start, end)) start end } return chunks } async function calcHash(sourceFile) { const spark new SparkMD5.ArrayBuffer() let offset 0 while (offset sourceFile.size) { const chunk sourceFile.slice(offset, offset CHUNK_SIZE) spark.append(await chunk.arrayBuffer()) offset CHUNK_SIZE } return spark.end() } async function uploadChunk(chunk, index) { const formData new FormData() formData.append(hash, fileHash.value) formData.append(index, index) formData.append(file, chunk) const controller new AbortController() cancelControllers.value.push(controller) try { await axios.post(/api/upload, formData, { signal: controller.signal }) uploadedIndexes.value.push(index) percent.value Math.round((uploadedIndexes.value.length / totalChunkCount.value) * 100) } finally { const i cancelControllers.value.indexOf(controller) if (i -1) cancelControllers.value.splice(i, 1) } } async function runTaskWithLimit(pendingList, limit) { let cursor 0 const runWorker async () { while (cursor pendingList.length) { if (pauseFlag.value) return const current cursor const item pendingList[current] try { await uploadChunk(item.chunk, item.index) } catch (e) { if (pauseFlag.value) return // 網絡失敗時重試一次 await uploadChunk(item.chunk, item.index) } } } const workers [] for (let i 0; i Math.min(limit, pendingList.length); i) { workers.push(runWorker()) } await Promise.all(workers) } async function startUpload() { const sourceFile file.value if (!sourceFile) return status.value computing-hash pauseFlag.value false fileHash.value await calcHash(sourceFile) const chunks createChunks(sourceFile) totalChunkCount.value chunks.length status.value checking const { data } await axios.post(/api/check, { hash: fileHash.value }) const uploaded data.uploaded || [] uploadedIndexes.value uploaded if (uploaded.length 0) { percent.value Math.round((uploaded.length / totalChunkCount.value) * 100) } const pending chunks .map((chunk, index) ({ chunk, index })) .filter((item) !uploaded.includes(item.index)) if (pending.length 0) { await mergeRequest(sourceFile) status.value done percent.value 100 return } status.value uploading try { await runTaskWithLimit(pending, 3) if (pauseFlag.value) return await mergeRequest(sourceFile) status.value done percent.value 100 } catch (e) { status.value failed } } async function mergeRequest(sourceFile) { await axios.post(/api/merge, { hash: fileHash.value, name: sourceFile.name, total: totalChunkCount.value }) } function pauseUpload() { pauseFlag.value true status.value paused cancelControllers.value.forEach((controller) controller.abort()) } function resumeUpload() { if (!file.value) return startUpload() } /script這段代碼里有一個很重要的細節(jié)上傳成功后立即把index推進uploadedIndexes數組并刷新進度這樣即使頁面中途刷新下一次續(xù)傳也能通過check接口拿回這些記錄。另一個細節(jié)是暫停時調用controller.abort讓正在傳輸的axios請求真的中斷而不是只把標志位改成true等著它自己跑完。之所以用AbortController而不是axios舊版的cancelToken是因為這是現(xiàn)在更推薦的標準方式axios新版也支持signal配置。4.3 后端服務完整實現(xiàn)server/index.js里放一個完整的Express服務包含前面說的三個接口。為了演示直接我合并文件時用時間戳加原始文件名作為輸出文件名但真實項目里建議換成hash加后綴可讀性差一點安全性高很多。const express require(express) const multer require(multer) const fs require(fs) const path require(path) const app express() const PORT 3000 const UPLOAD_DIR path.join(__dirname, uploads) const MERGED_DIR path.join(__dirname, merged) fs.mkdirSync(UPLOAD_DIR, { recursive: true }) fs.mkdirSync(MERGED_DIR, { recursive: true }) app.use(express.json()) const storage multer.diskStorage({ destination(req, file, cb) { const dir path.join(UPLOAD_DIR, req.body.hash) fs.mkdirSync(dir, { recursive: true }) cb(null, dir) }, filename(req, file, cb) { cb(null, ${req.body.index}.part) } }) const upload multer({ storage }) app.post(/api/upload, upload.single(file), (req, res) { res.json({ code: 0, message: chunk uploaded, index: Number(req.body.index) }) }) app.post(/api/check, (req, res) { const { hash } req.body const dir path.join(UPLOAD_DIR, hash) if (!fs.existsSync(dir)) { return res.json({ code: 0, uploaded: [] }) } const uploaded fs.readdirSync(dir) .filter((name) name.endsWith(.part)) .map((name) Number(name.split(.)[0])) res.json({ code: 0, uploaded }) }) app.post(/api/merge, async (req, res) { const { hash, name, total } req.body const dir path.join(UPLOAD_DIR, hash) if (!fs.existsSync(dir)) { return res.status(400).json({ code: 1, message: 分片目錄不存在 }) } const partFiles fs.readdirSync(dir) .filter((item) item.endsWith(.part)) .sort((a, b) Number(a.split(.)[0]) - Number(b.split(.)[0])) if (partFiles.length ! Number(total)) { return res.status(400).json({ code: 1, message: 分片數量不完整 }) } const outputPath path.join(MERGED_DIR, ${Date.now()}-${name}) const ws fs.createWriteStream(outputPath) for (const partFile of partFiles) { const rs fs.createReadStream(path.join(dir, partFile)) try { for await (const data of rs) { if (!ws.write(data)) { await new Promise((resolve) ws.once(drain, resolve)) } } } catch (e) { ws.destroy(e) return res.status(500).json({ code: 1, message: 合并失敗 }) } } ws.end() ws.on(finish, () res.json({ code: 0, message: merge ok, filePath: outputPath })) ws.on(error, (e) res.status(500).json({ code: 1, message: e.message })) }) app.listen(PORT, () { console.log(server running at http://localhost:${PORT}) })這里merge接口里對分片總數做了一個校驗前端傳過來的total必須等于實際part文件數量否則直接拒絕合并。這是防止“漏傳了幾個分片就去合并”的第一道防線但只靠數量校驗還不夠更好的做法是校驗所有分片大小之和是否等于原始文件大小或者合并后重新計算整文件hash。4.4 完整請求時序與體驗對照跑起來之后一個典型的斷點續(xù)傳請求鏈路是這樣的用戶選擇文件前端切片并計算hash狀態(tài)顯示“正在計算”。前端POST /api/check拿到服務端已存在的分片序號數組。前端過濾出剩余分片以3路并發(fā)逐片POST /api/upload。每傳完一片前端將index加入已上傳集合進度條前進一步。全部傳完后POST /api/merge服務端按index排序合并返回最終文件路徑。第一次全量傳可能要幾十秒。第二次選擇同一個文件再上傳check接口直接返回所有分片序號已存在前端不會發(fā)出任何upload請求直接走merge整個過程在1秒內完成這就是秒傳。中斷的體驗也能復現(xiàn)上傳到一半直接把后端進程殺掉前端會拋出一堆請求失敗然后暫停再重啟后端點續(xù)傳前端重新計算hash后通過check發(fā)現(xiàn)已經上傳了前10片就從第11片開始繼續(xù)傳剩余分片都是秒過。這個過程中真正網絡傳輸的只有中斷后剩余的部分前面的分片一個都沒有重傳。5. 斷點續(xù)傳DEMO里最容易踩的坑我?guī)湍阏砗昧?.1 hash怎么算才不卡UIdemo里我為了可讀性直接把hash計算放在主線程小文件沒問題一旦文件超過1GB你就能明顯看到頁面卡在“計算中”轉圈按鈕點不動。原因很簡單spark-md5的ArrayBuffer.append雖然每次只處理一個分片但整個計算循環(huán)占著主線程的事件循環(huán)Vue的響應式更新根本沒機會插進來。解決辦法是放進Web Worker。把分片讀取和spark-md5計算全部丟到worker里主線程只負責接收最后的hash字符串和進度。還有一個折中方案在主線程計算時每隔幾個分片await一次setTimeout或requestAnimationFrame讓出事件循環(huán)界面能保持響應但是對超大文件仍然不夠穩(wěn)。真實項目里我建議直接開Worker并且把“計算hash”也做成可中斷的否則用戶等了幾十秒想取消算到一半也退不掉。另一個性能問題是讀文件的方式。chunk.arrayBuffer()會把這一片完整拷進內存4MB一片沒問題但如果你大面積用整文件arrayBuffer幾個GB的文件直接內存溢出。demo里我始終基于切片來讀這一點不要改成file.arrayBuffer()一次性讀取。5.2 分片大小和并發(fā)數到底怎么配分片大小和并發(fā)數是斷點續(xù)傳最影響吞吐的兩個參數它們的合理值取決于你的用戶網絡環(huán)境。我整理了一份經驗值可以直接當初始基準場景推薦分片大小推薦并發(fā)數公網普通寬帶、用戶量大2MB-4MB3內網高速、服務端帶寬充足5MB-10MB5-8手機弱網環(huán)境1MB-2MB2-3公網環(huán)境并發(fā)數別開太大看起來快實際上容易觸發(fā)服務端連接數上限、代理超時一個分片失敗還會帶動整條連接排隊。弱網環(huán)境分片小一點這樣中斷發(fā)生時損失的字節(jié)更少重試速度也更快。這些值可以在后臺做成配置項按文件大小動態(tài)調整比如小于100MB走普通單請求大于100MB再啟用分片。還有一個容易忽略的參數是axios的超時時間。給每個分片請求設置一個合理的timeout比如30秒不要讓一個分片卡在服務端永遠等下去否則所有worker都等在同一片上后續(xù)分片全部排隊表現(xiàn)就是進度條卡住不動。5.3 斷點失效、秒傳失敗、進度不準逐個排查先看最常見的“斷點續(xù)傳不生效”重新打開頁面選擇同一個文件check接口返回的uploaded卻是空數組。這種情況十有八九是hash對不上檢查點有這幾個前端hash計算是否基于原始文件的完整內容有沒有因為切片邊界算錯。后端保存分片目錄時是否真的用了hash而不是用索引值拼錯路徑。同一個文件在前后兩次選擇時File對象的size和lastModified是否一致。如果文件內容沒變但hash不同多半是讀取邏輯里混入了無關字節(jié)。再看“秒傳失敗”所有分片明明都在前端還是把幾百個分片重新傳了一遍。先把check接口返回的數組打印出來如果返回的index是字符串而前端用includes判斷時類型不匹配就會全部判斷成不存在。這是JavaScript弱類型最容易踩的坑之一要么后端返回數字要么前端parseInt之后再去includes。進度不準的問題多數出在進度計算公式上。整個上傳過程其實包含hash計算和上傳兩個階段很多人只算了上傳階段的片數百分比用戶從0%開始等hash算了半天進度一直是0體驗很差。我的習慣是給hash計算分配一個固定小百分比比如5%上傳階段占95%然后總體進度等于hash階段完成后5%再加上uploadedIndexes.length / totalChunkCount * 95。最后一個坑是關于端口和代理的。Vite開發(fā)服務器默認5173后端3000如果跨域或不走代理前端發(fā)出的/api請求會404。我建議demo里統(tǒng)一通過vite的proxy轉發(fā)生產環(huán)境則用nginx把/api反代到后端這是最不容易出問題的部署姿勢。6. 演示之外我把這套方案搬進真實項目的經驗demo寫完之后我在自己維護的一個網盤類小工具里把這套邏輯改了改上線遇到了一些demo里沒有體現(xiàn)的問題分享幾個方向你可以作為下一步擴展的參考。第一是取消和續(xù)傳的狀態(tài)管理。demo里暫停用一個布爾標志位搞定但真實場景用戶可能在上傳中刷新頁面、切走再回來前端需要把上傳任務持久化到localStorage甚至IndexedDB記錄文件hash、分片序號和進度。重新加載后先恢復任務列表再逐個調check接口確定真實進度。這一步不做用戶中途刷新一次前面?zhèn)鞯钠腿皝G了”心智雖然服務端物理上還在但前端不知道從哪里繼續(xù)。第二是Web Worker里跑hash。我在worker里維護了一個“取消計算”的信號主線程傳給worker或者直接調用worker.terminate()用戶取消上傳時能把hash計算立刻停下來。這一改動對體驗的提升非常明顯尤其面對10GB以上的文件hash計算可能要好幾分鐘。第三是服務端的整潔度。文件系統(tǒng)做斷點記錄在單機demo里很舒服但上線后最好把“分片狀態(tài)”抽到Redis緩存文件本體放對象存儲或者分布式文件系統(tǒng)合并操作交給后端異步任務隊列去跑而不是在HTTP請求里同步讀寫大文件。同步讀寫會導致請求一直掛著網關很快超時斷開合并卻還在后臺寫文件返回包用戶根本收不到。如果只是要一個能跑的DEMO照著上面的代碼敲一遍就夠了如果要把斷點續(xù)傳做成生產功能分片存儲、失敗重試、秒傳校驗、權限控制這幾個模塊都值得單獨花時間打磨。我現(xiàn)在回看這個項目最大的收獲不是某個API有多難而是“把大問題拆成小問題、再對小問題分別處理”這個思路在斷點續(xù)傳里體現(xiàn)得尤其明顯。真遇到大文件傳輸需求時先別急著找現(xiàn)成的上傳組件把文件分片、hash指紋、斷點記錄這三件事想清楚代碼自然就出來了。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
色九月综合| 国产99久9在线| 情婷婷五月天在线| 99热免费精品| 91久久电影| 五月天婷婷激情在线色图| 高清激情av在线观看| 77777亚洲午夜久久| 丁香成人五月天| 亚洲精品五月| 天天爱天天做天天舔| 亚洲色图啪啪| 婷婷五月丁香图片人人操| 天天插天天插| 五月婷婷六月丁香五月| 久久婷婷五月免费视频| 天天草比天天爽| 啪啪啪大香蕉| 日本色天堂| 天天干在线播放| 婷婷成人综合| 日韩成人综合网| 激情婷婷啪啪| 婷婷开心久久| 天天射色五月天| 久久五月天大美女| 天天摸天天舔在线视频| 色五月首页| 岛国午夜视频| 欧美操我| 五月婷婷 激情五月| 成片免费播放| 欧亚洲在线高清视频| 91pornav在线| 丁香五月婷婷欧美激情-中文天堂最新版在线观看 | 亚洲激情综合| 少妇人妻人伦A片| 天天日天天做天天操| 狠狠爱综合网| 五月婷婷丁香伦理网| 人妻精品在线| 色八月婷婷| 婷婷精品免费久久| 久久五月天婷婷视频| 色五月五月婷婷| 久久大香免费| 操人91| 激情图片婷婷丁香五月| 1024国产| 午夜理论片最新午夜理论剧| AV 3P| 99在线精品视频观看免费下载| 99在线69| 狠狠色狠狠鲁| 美女丁香五婷婷| 99国产性感视频| 春色激情| 久久99精品久久久久久青青AR| 天天久综合网永久入口17v | 亚洲综合视频网| 大香蕉九九| 九艹在线| 日韩一区二区在线播放| 久久99热精品a片在线观看| 婷婷基地成人五月天| 五月婷色| www.五月瑟| 婷婷综合色五月天| 激情五月天色色网| 欧美日韩成卜| 91.com男女操| 人人射av| 日本天天色| 日韩一级网站| 六月激情网| 黄桃AV无码免费一区二区三区| 亚洲熟妇AV乱码在线观看| 狠狠夜夜五月丁香| 人妻久久久久久久久| 五月天激情婷婷小说| 人人视频色| 五月婷婷婷| 影音先锋91资源站| 免费无码毛片一区二区A片 | 大香蕉啪啪啪啪啪啪| 久热丁香| 桔色成人官方网站| 久久久27操| 久久婷婷热| 蜜桃人妻无码AV天堂三区| 青吴乐视频| 激情五月,激情综合网| 亚洲色情久久| 色五月播五月| 综合在线观看99| 久久九九中文字幕| 97色婷婷五月天| 色色色婷婷| 色婷婷五月天| 丁香九月激情| 五月婷婷在线视频观看| 亚洲午夜成人av电影网| 99re熱| 丁香婷婷五色月| 久久五月丁香| 久9久视频精品| 九九热10| 五月丁香六月情| 99这里只有精品| 色色六月| 五月丁香啪啪激情| 狠狠干,狠狠操| 婷婷五月精品| 亚洲国产成人裸舞| 丁香五月婷婷图片综合| 五月丁香婷婷色播无码| 99热这里只有99| 播五月丁香六月| 六月婷婷操逼| 性爱先锋AV| 婷婷色成人| 精品久久久人妻| 婷婷五月丁香四射| 五月天婷婷色播| AV成人在线播放| 久久99久久99精品,久国产,久久精品免费,99久在线,久久久久国产精品免费网站,9 | 色丁香五月婷婷| 97成人在线视频精品| 五月亭亭六月色| 人人草人人看| 色色色色色综合| 欧美色五月| 五月天婷婷Av| VfJxEwPH| 日韩久操婷婷| 激情伊人五月天| 国产视频色色色色色色色| 久久五月视频| 激情五月婷婷丁香| 99爱免费在线观看| 超碰成人公开| 五月天成人在线播放丁香| 激情九月婷婷| 婷婷播播五月天| 久久性爱视频| 六月丁香婷婷色69| 99精品久| 热久久这里只有精品| 五月婷久久久久综合| 色婷婷丁香五月天在线视频| 国产婷伊人| 国产色色在线| 99久久精品免费精品国产_国产精品久久久久久_国产在线|日韩_久久国产精品电影 | 草草操操| 婷五月天天| 色五月天激情| 日本天天综合| 丁香五月婷婷综合激情啪啪啪| www.色婷婷。com| 天天操天天插| 欧美亚洲操逼| 啪啪五月综合| 五月丁香色| 亚洲激情AV| 日日杆天天| 五月婷婷黄色| 9色婷婷| 丁香五月婷婷六月婷| 另类激情综合| 五月天综合图片| www91色网站| 色色综合视频| 日本久久婷婷| 亚洲国产精品SUV| 狠狠草婷婷| 伊人激情AV一区二区三区| 337p大胆噜噜噜噜噜91Av| 996热re视频精品视频| 天天操婷婷| 97色婷婷成人综合在线观看| 国产露脸150部国语对白| 婷婷五月丁香伊人网| 97亚洲精品| 五月婷丁香| 五月婷婷六月天| 狠狠色丁香| 99视频精品8 | 激情久久五月天| 五月天激情图片| 丁香六月av| 婷婷五月天成人视频| 五月婷婷视频| 激情五月婷婷啪啪| 日韩在线视频网站| 久久婷婷丁香花综合网| 成人丁香五月天| 青青草婷婷综合五月| 99碰碰碰| 亚洲无AV在线中文字幕| www色五月| 天天摸,天天爽| 人人操超碰| 综合色五月天| 久热网在线视频| -91九色大屁股| 丰满少妇猛烈A片免费看观看| 九色综合网| 天天做天天爱综合| 九九热只有精品6| 色六月 婷婷| 九热精品| 天天操婷婷| 色色热99| 97超碰人人操| 婷婷色吧| 大香蕉丁香| 另类在线| 亭亭五月丁香五月天激情| 999热在线视频| 亚洲丁香花色| 日韩欧美颜射| 99在线视频。| 91丨九色丨高潮丰满日本| 26uuuu精品一区二区| 色五月偷偷| 国产肥白大熟妇BBBB视频| 综合噜噜| 狠狠色综合网站久久久久| 小视频在线亚洲| 9色在线视频精品观看| 99精品网| 青青草成人网| 久操热| 婷婷激情小说| 久久金品黃色| 婷婷五月综合亚洲| 久久99热这里只有精品| 日韩操逼小电影| site:wpjngj.com| 4399亚洲视频| 九九色图| 91丨九色丨白浆| 激情VA视频| 五月婷婷婷婷婷婷艺术| 婷婷性爱无码视频| 亚洲五月婷天天操| 天天做天天要天天爽| 日本色道视频网站| 欧美噜噜免费观看| 六月亚洲| 99亚洲色| 色综合99| 色色五月婷婷久久| 久久九九网| 去色色五月天| 五月天婷婷久久| 婷色五月天| 激情亚洲网| 五月天色裸体视频| 夜夜撸日日操| 九九aV| 爱久综合| 久99热| 色婷婷欧美在线| 婷婷射综合| tingtingseav| 人人操超碰| 伊人综合网站| 乱精品一区字幕二区| 精品网站:999WWW| 常久最新免费的色吊丝| 久99热| 深爱婷婷色| 91久久久久久久| 日本123区日韩欧美不卡在线看| 国产日产亚系列精品版优势| 无码AV免费精品一区二区三区| 婷婷激情综合网| 人人草人人爱| 国产亚洲精品久久一区二区三区| 色 色 色综合com| 艹| 国内婷婷丁香社区在线播放| 五月在线婷色| 天天草婷婷五月| 九九色中文| www.五月天婷婷| 91热久| 六月丁香成人| 婷婷丁香五月天影院 | 午夜激情四射影院| 激情婷婷。| 91婷婷视频| 国产XXXX搡XXXXX搡麻豆| 亚洲成av人影院| 欧美日本韩国亚洲| 91人人妻人人操人人爽| 色婷婷AⅤ| 色色色网站| 伊人午夜综合色啪| 九九精品网| 五月丁香在线综合| 女人被男人吃奶到高潮| 操操人人| 狠狠色婷婷7777久| 九九无码AV| 激情五月婷婷综合网| 婷婷最新地址| 影音先锋91资源站| 伊人激情| 婷婷丁香五月欧美人| 亚洲激情综合五月婷婷啪啪| 五月激情天天干| 久久视频这里有精品99| 激情99热| 色色色综合| 色五月丁香91| 99狠狠色| 色五月婷婷视频| 狠狠插日日干撸| 五月丁香五月综合欧美| 99性色| 五月丁香六月婷| 精品成人在线| 婷婷内射视频在线| 五月婷婷熟女| 亚洲婷婷激情综合激情999精品| 91色噜噜狠狠狠狠色综合| 色色五月婷| 国产一区二区三区影院| 超碰国产AV| 超碰成人电影| 国产日日操夜夜操的肉棒视频| www.五月丁香| 婷婷丁香五另类网站| 26uuu亚洲色| 国产欧洲欧洲精品久久| 九九性视频| 先锋影音男人的天堂AV| 成片免费观看视频大全| 婷婷五月骚厕所| 91日日日| 国产毛片操B| 开心五月婷婷婷美女| 综合激情啪啪| 5五月综合网亚洲| 六月婷婷五月天| 欧美A级成人婬片免费看理论| 久热久re| 婷婷精品在线| 欧美99热| 天天噜天天爱| 久久久久人妻网址| 婷婷五月综合免费在线| 免费成人va| 开心五月深爱婷婷| 国产精品18久久久| 五月色婷婷中文字幕| 丁香五月婷婷精品视频| 9+1视频网址| 久久九网| 888精品福利地址| 99成人免费热视频| 风流少妇A片一区二区蜜桃| 日日噜噜久久婷婷五月天| 99re熱| 婷婷成人五月天成人文学小说| 五月丁香婷婷成人网| 丁香婷婷六月| 97在线精品| 97超级啪啪在线观看| 五月婷婷丁香| 97欧美在线| 狠狠干.com| 开心五月婷婷激情| 色色色综合| 婷婷综合中文| 色欲丁香久久| 激情五月综合亚洲另类| 九九Y精品热播| 无码动漫av| 五月青青草综合| 97香蕉碰碰人妻国产欧美| 色综合久久无码| 五月丁香少妇A| AV在线观看网站| 色级婷婷| 五月婷婷基地| 99@久久@99精品视频| 18av天堂| 丁香五月天激情视频| 色色丁香五月| 日日操夜夜撸| 久婷婷色| 五月天婷综合| 丁香五月天狠狠操| 五月天狠狠色| 综合色网站| 久久免片| 亚洲AV无码影院| 婷婷噜噜| 国产91视频| 色五月婷婷五月久久| 超碰久热| 99热99在线| 色色99| 中美日韩成人在线| 日本三级黄色大片| 超碰精品在线| 五月亭亭开心网| 色婷婷综合久久久久| 大香蕉久久婷婷| 97久操视频| 这里只有精品免费| www.五月天婷婷| 色婷婷丁香五月天激情综合网| 婷婷综合激情| 久久女人九九| 五月丁香六月综合激情| seav天堂| 天天色月| 网站免费一站二站| 激情涩涩网| 精品婷婷| 五月综亚洲| 亚洲精品久久久无码| 热的国产,热的综合,热的有码 | 开心婷婷五月| 中文字幕在线不卡| 99热超碰在线| 天天日天天操天天干| 精品一二三区久久AAA片| 就爱啪啪婷婷| 五月丁香天堂| 六月婷婷av| 丁香婷婷六月激情文学| 777精品久无码人妻蜜桃| 色五月婷婷激情五月| 玖玖色综合色| 人妻久久久| 欧美丁香五月97色| 蜜桃人妻无码AV天堂三区| 2025最新亚洲激情在线| 强奸幻女毛片| 午夜成人AV在线| 一级片sese片.COM| 丁香五月婷婷啪啪视频| 成人网在线视频| 99re在线视频精品,这里只有精品18,| 国产成人精品一区二区三区视频| 草逼大片| 玖热精品综合视频| www.丁香六月婷婷久久天堂影院.con| 婷婷五月色| 色综合婷婷| 99在线免费观看| 天堂AV在线看| 九九碰九九爱97| 国产偷人爽久久久久久老妇APP| 夜夜撸天天操| 亚洲精品色色| 开心久久网婷婷| 色五月丁香婷婷| 激情五月天色婷婷综合| 伊人久久艹| 免费三级黄色| 91精品婷婷国产综合久久| 狠狠擼综合| 在线99精品| www.99视频| 国产日韩欧美| 五月色情网| 国产成人在线不卡AV| 天天干一干| 人人视频人人干人人做| 五月丁香六月婷婷色| 人妻久久婷婷| 5月婷婷6月丁香aV| 色碰干| A片试看50分钟做受视频| 婷婷五月天视频免费在线观看| site:wpjngj.com| 国产亚洲色婷婷久久99精品91 www.riverspirits.org www.hnnun.com www.changh | 深爱网深爱综合网| av色婷婷| 五月成人天| 九九热这里精品| 五月婷免费视频| 天天干天天拍| 9999三级片| 丁香婷婷伊人| 人妻久久做| 大香蕉大香蕉在线影院| 日韩AC在线免费观看| 丁香五月Av| 另类视频五月天| www.五月婷婷久久.com| 五月婷六月综合在线观看| 日本三级毛片| 91日综合欧美| 97成人丁香| AV九九| 国产avapp 网| 五月开心久久| 五月丁香六月婷婷久久| 丁香六月激情综合网| 91久久九久久九久久九久久九久久 | 五月 婷 久| 五月丁香六月情| 丁香五月激情综合| 色五月丁香六月欧美综合| 婷婷五月黄色激情在线| 色婷婷狠狠18禁| 青青草婷婷久久| 黑人巨粗进入警花疼哭A片| 五月伊人网| 激情五月天开心总和网| 色婷婷丁香五月天| 五月丁香中文字幕| 五月婷在线色视频| 丁香婷婷综合激情五月色,开心五月丁香花综合网,激情综合五月亚洲婷婷,五月天 | 九九激情| 天天摸天天透天天舔| 九九在线免费观看| 99极品视频| 色5在线| 另类精品视频在线观看| 丰满少妇乱A片无码| 成人在线视频一区| 五月天婷婷在线AN| 婷婷综合色五月天| 伊人久久大香线蕉av一区| sewuyuejiqingwang| 久热99| 久久久性爱网| 99久在线视频| 91男同| 97性视频| 妇激情基地| 中文字幕在线免费| 中文字幕av久久爽一区| 五月丁香六月久久| 538午夜激情| 97人妻碰碰中文无码久热丝袜| 久久色五月| 婷婷五月丁香欧洲| 夜夜爱伊人| 色私五月婷婷| 99色色网| 五月丁香 啪啪| 丁香九月婷| 超碰免费人人| 热99精品视频观看| 亚洲色五月天是什么| 色综合爽| 丁香五月婷婷久久综合激情网| 欧美97色| 色情五月丁香婷婷网| 日本精品。999| 五月婷久久| 亚洲婷婷六月天| AV五月婷婷露脸| 思思re99视频在线观看| 國語久久婷| 67194国产| 99热免费| 婷婷综合中文字幕| www.99热在线| 久久99久久久久久| 色欲色香综合网| 人人干人人看| 婷婷色色综合| 久久伊人9| 久九色| 激情丁香五月婷| 五月天久久丁香| 综合图片色色| 九九热只有精品| 色色欧美色色| 97成人在线视频精品| 超碰激情网| 婷婷区日本| 伍月婷丁香花全集| 免费观看亚洲AV片| 精品人妻在线免费观看| 视频1区2区| 五月天婷婷色五月天| 天天日人人爽| 婷婷五月在线视频| 中文字幕日产A片在线看| 欧美日本国产| 日本色天堂| 九九久久99| 色婷婷五月天偷拍| 成人精品视频99在线观看免费 | 亚洲综合激情五月天婷婷| 五月深爱婷婷| site:pnnrt.com| 9+1视频网址| 丁香五月色色婷| AV在线大香蕉| 婷婷五月天成人小说| 伊人激情啪啪| 欧美日本不卡黄色片| 丁香六月婷婷社区| 六月丁香成人| 色五月丁香婷婷久草| 天天干天天干天天干| 综合99在线| 91久久综合| 天天色中文字幕女优AV| xxxx久| 五月天激情婷婷小说| 色色色色五月天| 久久婷婷超碰| 五月婷婷伊人网| 精品久热69| 国产成人综合亚洲| 爱之国产色情综合| 五月婷婷中文字幕| 一区二区aV电影免费看| 久久婷婷色丁香| 99热e| 丁香操逼| 激情五月综合色婷婷| 黄色99网| 亚洲情a| av人人干| 免费97碰碰| 日韩操| 色色99| 色99日韩| 欧美日本黄色| 五月婷婷,六月婷婷| 丁香五月婷婷天激情| 欧美日韩一区二区三区四区| 秋霞av不能| 六月丁香综合网| 五月丁香综合在线| 综合激情在线| 超碰人人操| 91碰碰碰久久久久| 常久最新免费的色吊丝| 91男人操女人视频| 婷婷丁香五月天在线视频| 狠狠草在线观看| 九九婷婷综合| 五月激情网站| 91成人看片| 九九热视| 狠狠色网| 天天影视天天爽天天草| 99在线视频喷水| 婷婷五月亚洲一本在线丁香| 色综合99无码 | 午夜天堂一区人妻| 情五月亚洲婷婷| 天天激情综合| 婷婷五月综合在线| 亚洲精品无码久久| 色色色色综合网| 婷婷色一二三区波多野结衣| 五月天最新网| 很很色丁香久久停停| 婷婷丁香五月网| 9久操| 色五月婷婷色五月婷婷色五月婷婷| 日韩五月婷婷久久| 激情综合丁香五月| 亚洲超级碰| 婷婷五月天亚洲五码| 影音先锋男人资源站一区二区| 狠狠干,狠狠操| 久婷自拍视频| 久久小说网| 深爱激情九九五月天 | 97超碰免费超级在线观看| 99小精品| 亚洲中字AV电影在线网站| 精品人妻久久久| 91久久国产自产拍夜夜91久久精品文字>91麻豆精品国产 | 狠狠噪| 国产99视频永久免费| 七月婷婷色香综合网| 狠狠插日日干撸| av在线不卡播放| 亚洲网视屏| 亚洲综合字幕色色| 色五月婷婷操逼| 五月婷婷黄网站大全| 只有久久精品免费| 中文字幕av久久爽| 1024操逼视频| 色婷婷影| 婷婷情色五月| 天天爽天天干| 色婷婷五月天亚洲| 91性交在线播放| 天天视频亚洲| 欧美色宗和激情| 中文av网| 亚洲另类噜噜| 狼人狠狠操| 五月天婷婷综合网| 99热只有这里才是精品| 婷婷五月天AV在线| 国产精品久久久久久久久久免费| 99热中文字幕久久| 丁香色婷婷| 噼里啪啦完整版中文在线观看| 欧美综合五月丁香五月天| 久久天堂婷婷五月| 狠狠操综合| 激情小说视频图片网| 玖玖午夜视频| 色综合99| 91丨九色丨白浆| 亚洲色欲欧美一区二区三区| 激情图片久久| 无码激情AAAAA片-区区| enecarbon-materials.comWu染请涟系Bao护@wip1688 | 特黄三级片| 开心激情站婷婷五月天| 97干在线播放| 91久女| 色婷婷基地| 国产乱妇乱子伦| 99九色视频在线观看| 丁香五月之久操视频| 色五月天丁香| 五月婷婷说| 婷婷五六月丁香| 久久免费丁香| 日本黄 色 片| 99爱视频免费| 天天综合网站| 深爱1激情网| 思思热久久阴99| 97电影99热| 婷婷综合中文| 丁香五月天堂网| 色色色综合| 26uuu91| 五月天婷a在线| 色域五月丁香| WWW.99热| 亚洲九N| 91狠狠综合久久久| 激情五月婷婷五月| 欧美性生交XXXXX无码小说| 日本色色网站| 色婷婷五月婷婷五月婷婷五月| 色99日韩| 无码髙清| 婷婷五月天大香蕉在线视频观看| 国自产拍偷拍精品啪啪一区二区| 99干视频| 激情综合网激情五月天| 五月天久久综合婷婷丁香| 九九热AV| 婷婷色网站| 秋霞丝袜啪啪啪| 色婷婷在线视频观看| 亚洲亚洲人成综合网络| 五月婷婷综合网| 天天摸天天肏| 精品久久9| 五月婷婷在线视频观看| 久久视频在线| 天天天天色天天天天天干| 性 色 婷婷| 五月花婷婷在线精品视频| 六月丁香啪啪啪| 天天揷综合网| 五月天婷婷乱| 五月激情婷婷图片基地| 中文字幕,综合,91| 欧美成人日韩| 91大神在线免费看视频全集男男一起操| 丁香综合日产精品久久| 色偷偷AV亚洲男人的天堂| 六月丁香啪啪| 丁香婷婷久久 | 影音先锋美国A| 久久精品99国产精品日本| 五月激情天| 久久99网站| 日本狠狠干| 九九热视频在线观看| 日日夜夜天天| 婷婷中文字幕| 久久黄色片| 狠狠爱深色婷婷综合| 婷婷五月欧美综合| 草草色情综合网| 黄色高清无码| 狠狠 久久| 久久婷婷丁香视频网| av成人在线播放| 91人人澡人人爽人人看| 9999三级片| www久| 99视频超级精品| 日本女天天爽| 婷婷六月插屄激情| 婷婷射图五月天| 亚洲天堂九九九| 9久久精品视频| 操久久网| 九九这里只有精品在线视频| 亭亭色网| 亭亭玉立国色天香| 五月婷婷久久久| www999日韩精品| 久久久久久欧美精品se一二三四| 婷婷丁香77777| 天天射影视综合网| www.五月天婷婷| 影音先锋噜一噜| 色噜噜狠狠色综无码久久合欧美| 日产精品久久久久久久蜜臀| 天天干在线播放| 婷婷五月天情色| 91Chinese在线| 亚洲射激情| 激情婷婷啪啪| 五月天婷婷基地| 欧美S码亚洲码精品M码| 强奸幻女毛片| 婷婷激情综合| 婷婷综合五月色播| 人人摸人人澡人人| 五月天开心网| 天堂呦 呦百度搜索-百度搜索| 九九婷| 五月激情网站| 狠狠va| 99热亚洲| 99久久婷婷国产综合精品草原| 97操碰在线97| 色9999日韩国产| 色噜综| 久久人人九| 三年高清大片免费观看国语| 综合99在线| 五月婷婷丁香啪啪| 狠狠插日日干撸| 色五月婷婷久久| 色五月 婷婷, 大香蕉| 大香蕉婷婷久久| 五月天婷婷丁香蜜桃91| 天天日婷婷| 色婷婷影视| 99九九精品视频推荐| 少妇激情基地| 99免费视频网| 亚洲综合五月天婷婷丁香| 色偷偷人人| 天天在线天天综合网色| 五月婷婷激情综合| 色五月婷婷成人| 99人碰碰碰| 婷婷婷婷婷婷婷五月丁香| 激情五月天 婷婷| 啪啪五月综合| 色天天久婷婷| 色99在线| 国产中文字幕在线视频免费观看 | 色婷婷内射| 国产免费一区二区三州老师F1F1| 综合天堂AV久久久久久久| 九月色婷婷| 玖玖婷婷精品| 婷婷一本和五月丁香| 丁香蜜臀黄色婷婷五月天| 97超美国视频在线观看| 综合亚洲色色| 黄网免费看| 色五月开心久久网| 色情播放| 人人操AV| 日韩999| 99热国产免费| 草久私拍| 26uuu亚洲色| 婷色五月| 欧美性爱五月天| 色综合久久88色综合天天看| 色色五月天 亚洲| 人妻系列久久久久久久久久久 | 日本在线wwww| 五月天婷婷丁香视频| 91一起操| 色五月综合在线| 任你搞网站| 日韩啪图| 综合激情五月天六月婷免费视频| 欧洲亚洲精品| 人妻人人操| 丁香六月成人| 五月婷性爱| 天天操天天操综合| CHINESE熟女老女人HD视频| www.99婷婷| 激情99热| 青青福利网| 99免费在线视频| 91狠狠色色丁香婷婷综合久久| 99热在线只有精品| 在线观看日韩12345区| 偷拍视频五月天| 国产偷人爽久久久久久老妇APP| 99亚洲综合| 国产欧洲欧洲精品久久| eeuss人妻| 五月天婷婷在线观看| 亚洲激情四射| 超碰在线99| 九九热精品| 99热在线观看精品| 丁香久久| 五月丁香婷婷基地| 五月婷婷高清| 综合色图区| 婷婷五月天在线观看av| 麻豆AV一区二区三区| 狠狠婷婷爱| 亚洲欧洲中文日韩久久AV乱码| 中文字幕日韩无码制服诱或| 日本熟妇乱妇熟色A片蜜桃| 第四色婷婷五月| 婷婷色五月综合| 99色视频免费在线规看| 日本熟女啪啪| 天堂综合久| 五月丁香色综合| 五月天综合久久| 97se视频在线| 色优久久| 色播五月丁香综合| 色逼综合网| 国产激情在线观看| 色色色色色色网站| 大香蕉五月天婷婷丁香91| 自拍偷窥99热| 99热九九这里只有精品10| 中文字幕丰满孑伦无码专区| 天天摸人人摸| 婷婷九月丁香天堂丁香天堂| 超碰免费人人| WWW五月婷婷| 91久久国产自产拍夜夜91久久精品文字>91麻豆精品国产 | 变态 另类 在线 | 狠狠操狠狠色| 久久色五月天| 99久久九九| 在线看的免费网站| 777久久综合视频| 真实的国产乱XXXX在线91| 欧美色频| 日本色频| 伊人九热| 玖玖在线| 狠狠狠狠狠狠狠狠狠狠狠色宗合图片 | 婷婷五月丁香手机在线视频| 成人在线日韩欧美| 激情综合婷婷| 婷婷五月噜噜| 亚洲一级色电影| 99操| 欧美色婷婷| 99色最新在线视频| av操B网站| 99精品热| 五月天婷婷黄色| 五月婷婷色影院| 丰满少妇乱A片无码| 五月婷婷激情综合av| 欧美激情丁香五月| 97丁香视频| WWW,五月| 婷婷五月天激情五月天深爱五月天| 久久五月丁香婷婷| 成人国产欧美大片一区| 五月丁香无码| 激情久久五月网| 亭亭色网| 我淫我色婷婷五月天激情四射| 99A级片| 五月丁香婷婷婷婷综合网| 色色色色色色色色色色色色色97| 99久久99久久综合| 五月天色区| 五月丁香啪啪| 美女伊人久久| 全部老头和老太XXXXX| 色婷婷精品视频| 五月天激情国产综合AV| 97色97干| 日本97在线观看| www.99热这里只有精品| 婷婷五月天天天| 婷婷中文在线| 婷婷五月天丁香成人社区| 大地9中文在线观看免费高清| 色五月涩涩婷婷| 婷婷五月天成人基地| www.色五月| 久99久视频免费观看| 午夜天堂一区人妻| 五月婷婷综合网| 国产.亚洲.欧洲视频在线| 色综合久久88色综合中文字幕| 激情综合色| 39视频第二区| 久9精品视频在线| 久久婷婷色| 九九AV在线| 婷婷五月丁香五月天| 色婷婷综合久色AV五色最新| 亚欧州精品视频| 真实熟女-91九色| 亚洲欧洲中文日韩久久AV乱码| 婷婷激情性爱| 色色色热热热| 思思久热6| 色色丁香五月婷婷| 99热香港| 九九AV| 婷婷色五月丁香六月欧美啪| 操比激情五月| 99热伊人综合| 无码激情AAAAA片-区区| 五月天色图| 色区久久| 99九九在线视频| 五月婷婷视频在线观看| 六月婷婷九月丁香亚洲综合| 激情婷婷另类| 丁香五月成人论坛| 久热这里这里有精品| 色欲人妻综合aaaaaaaa网| 五月丁香久| 中文字幕九九九九| 人人搡人人| 丁香五月天婷婷91| 丁香六月激情综合网| 99爱在线| 9999热在线| 超碰婷婷五月| 97超碰色| 91精品刘玥| 韩国真做片在线观看| 色色色com| 丁香婷婷久久 | 六月丁香啪啪啪| 大香蕉婷婷五月| 婷婷丁香97| 五月丁香色| 亚洲综人色综网| 97人人操人人爽| 67194国产| 99热在线精品观看| 五月婷婷中文字幕| 色色吧综合| 九九热欧美| 人人亚洲| 激情视频网址| 国产在线6| www.狠狠操.con| 亚洲九九九九| 久久五月激情| 丁香六月婷婷| 日本高清久| 九九热黄色| 色欲一区二区三区精品A片| 久久色情| 伊人网色婷婷五月天| 久热99狠| pom538精品视频| 欧美精产国品一二三区| 亚洲无码成人网| caopeng97日韩| 丁香五月五婷| 大香蕉久热| 五月婷婷激情综合网| 五月婷婷丁香六月| 青吴乐视频| 碰97久久| 五月丁香六月成人| 伊人网欧美在线男人天堂五月丁香| 婷婷激情中文综合| 26uuuu精品一区二区| 久久性爱视频这里只有精品| 99视频在线精品| 五月天另类小说| 婷婷五月在线视频| 色综合播放| AV在线大香蕉| 综合狠狠伊人| 五月天婷婷视频30| 国产激情综合五月久久| 中文字幕在线日亚洲9| 五月婷婷丁香六月| 超碰人人在线观看| 五月久熟女| 五月丁香日本一抹本| 天天插天天| 99免费在线视频| 能看的av网站| 91日在线视频| 久久东京热婷婷五月| 亚洲综合色网站| 99在线精品免费视频| 婷婷射丁香| XX色综合| 任你擦免费视频| 激情婷婷内射| 婷婷无码五月天| 91久久婷婷| 91丨九色丨国产打屁股网站| 五月激情综合网| 森林影视大全,最好看的2019年视频| 色久丁香五| 婷婷五月天丁香花| 九九热在线视频| 欧美日韩精品一区二区三区钱| 色婷婷超碰| ss99热| 久久综合影院| 色欲五月丁香| 色色五月天激情| 亚洲人人操BD| 日日干天天爽| 婷婷综合在线视频| 丁香五月天信号| 天天做天天爱天天爽| 伊人在线视频| 亚洲AV免费在线| 丁香五月婷婷综合激情啪啪啪啪啪啪啪| 毛片新网地| 亚洲第一视频 久久| 久久伊人大香蕉| 97涩婷婷| 五月天婷婷色紫薇阁| 任你搞网站| 成人五月天综合网| 色九网| 婷婷五月精品中文字幕| 天天插天天日| 日本99在线| 五月亭亭综合五码| 色婷网| 99精品久久久| 五月丁香婷爱在线| 欧美性生交XXXXX无码小说 | 午夜激情综合| 五月婷婷亚洲综合在线 | www夜夜操| 国产阿姨日皮艹逼内射视频| 九九热99热| 色五月激情五月| 嫩草AV久久伊人妇女超级A| 久久伊人五月天| 91男人操女人视频| 久久99热这里只有精品23| 91综合色| 97精品综合久久内射| 大香蕉天堂| 色五婷婷在线视频| 人妻免费网站| 色亚洲视频| 天天拍天天操| AAA久久| 五月婷婷丁香五月天| 久久婷婷五月综合色丁香花| www.91.com处女在线直播| 丁香五月另类色婷婷麻豆| 成人网站高清无码| 综合色激情| 日日操夜夜擼| 激情五月综合色婷婷| 97五月天| 天天爽爽日日做做| 丁香成人五月天| 、激情六月天| www.五月婷婷| 91黄址| 亚洲av网站| 99A级片| 婷婷午夜综合| 九九干视频| 激情婷婷六月天| 五月天之色情综合网| 丁香婷婷成人网| 99碰碰碰| 可以看的AV网站| 丁香五月另类小说| 亚洲国产精品VA在线看黑人| 综合久久丁香婷婷,五月婷婷六月丁香,开心激情综合网,六月丁香在线观看,婷婷丁 | 51XX午夜影福利| 日本爆乳片手机在线播放| 天天肏高清在线| 亚洲综合草草| 亚洲啪啪网| 青青草tp| 亚洲色久| 人妻内射视频| 丁香五月婷婷五月天在线| 99色视频| 五月婷婷六月激情在线| 97色五月天| 久久婷婷的综合色丁香五月| 亚色网站小视频| 大香蕉婷婷| 天天综合图片| 日韩不卡123| 婷婷瑟瑟五月天| 丁香九色不卡aaa| 丁香五月婷婷六月丁香| 亚洲成人在线观看av| 天天xxxxxx天天日| aaa9区免费在线观看| 激情丁香五月天| 免费看成人747474九号视频在线观看| 色情久久久| 色五月天天在线观看资源站| 欧美成人精品A片免费一区99| 九九这里精品| 久久婷婷六月综合综合| 天天舔天天摸| 丁香婷婷久久| 亚洲第一成人无码A片| 婷婷丁香五月天在线| 9 1在线视频| 婷婷五月天激情四射| 久久免费精品小视频| av大香蕉| 五月天色视频| 人人干99| 97色色网| 国产精品久久7777777精品无码| 九九亚洲无码| 99热只有这里有精品| 777精品成人a v久久| 婷婷丁香五月基地| 很很干夜夜干| 91精品久久久久| 久久综合99综合| 少妇人妻人伦A片| 九月色婷婷| 99热在线观看成人| 色婷婷五月综合在线| 丁香蜜臀黄色婷婷五月天| 狠狠爱五月婷婷综合六月| 五月丁香激情四射| 丁香六月激情综合网| 久久久久久9热不雅视频| 三级三久久线久久99久目本WW| 99热在线中文字幕| 婷婷中文网站| 在线播放中文字幕| 亚洲免费婷婷| 国产在线6| 黄色毛片精品| 99国产精品白浆在线观看免费|