精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

聊聊大文件分片上傳和分片下載

開發 前端
在上傳大文件時,需要考慮服務器的處理能力和存儲空間,以及安全問題。同時,避免并發上傳相同文件以確保續傳的準確性。可以使用唯一的文件標識符或用戶會話標識符來區分。

1. 文件流操作

在軟件開發中,我們會看到各種形形色色的文件/資源(pdf/word/音頻/視頻),其實它們歸根到底就是不同數據格式的以滿足自身規則的情況下展示。說的更淺顯易懂點,它們都是數據,并且最終都會以二進制形式展示。也就是說,我們的各種操作都是在處理數據。那么處理文件也是如此。

在前端開發中,文件流操作允許我們通過數據流來處理文件,執行諸如讀取、寫入和刪除文件的操作。

在前端開發中,文件可以作為數據流來處理。數據流是從一個源到另一個目的地傳輸的數據序列。

Blob 對象和 ArrayBuffer:處理二進制數據

在前端處理二進制數據時,有兩個對象是繞不開的。

  • Blob 對象[1](Binary Large Object)對象是一種可以在 JavaScript 中存儲大量二進制數據的對象。可以通過構造函數創建 Blob 對象,或者通過其他 API(如 FormData 對象[2])生成。
  • ArrayBuffer[3] 是 JavaScript 中的另一種對象類型,它們可以存儲二進制數據。ArrayBuffers 通常用于較低級別的操作,如直接操作和處理二進制數據。

使用 FileReader 讀取文件

FileReader 是一個前端瀏覽器 API,允許我們異步讀取文件內容并將其轉換為可用的數據格式,如文本或二進制數據。

它提供了如 readAsText()[4] 和 readAsArrayBuffer()[5] 等方法,可以根據我們的需要進行選擇。

圖片圖片

使用案例

下面,我們來用一個例子來簡單說明一下FileReader的使用方式。

import { ChangeEvent, useState } from 'react';

function FileInput() {
  // 讀取文件內容到 ArrayBuffer
  function readFileToArrayBuffer(file: File): Promise<ArrayBuffer> {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();

      // 注冊文件讀取完成后的回調函數
      reader.onload = function (event) {
        const arrayBuffer = event.target?.result as ArrayBuffer;
        resolve(arrayBuffer);
      };

      // 讀取文件內容到 ArrayBuffer
      reader.readAsArrayBuffer(file);

      // 處理文件讀取錯誤
      reader.onerror = function (error) {
        reject(error);
      };
    });
  }

 
  // 處理文件選擇事件
  function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0]; // 獲取選擇的文件

    if (file) {
      readFileToArrayBuffer(file)
        .then((arrayBuffer) => {
         // 此處已經能拿到文件的`arrayBuffer`信息,也就是Blob數據
        })
        .catch((error) => {
          console.error('文件讀取失敗:', error);
        });
    } 
  }

  return (
    <div>
      <input type="file" notallow={handleFileChange} />
    </div>
  );
}

export default FileInput;

在上面的代碼中,我創建了一個名為 FileInput 的函數組件。該組件有一個文件選擇框。當用戶選擇一個文件時,文件內容會使用 FileReader[6] 讀取到 ArrayBuffer。然后在對應的回調中就可以處理對應的Blob信息了。

當然,我們這里是利用FileReader的readAsArrayBuffer將文件內容轉換成(ArrayBuffer)。這樣我們可以更好的進行分片處理(這個后面會講)。其實,我們還可以使用例如readAsDataURL()將資源變成一個url,然后在頁面中顯示。

具體的顯示方法取決于文件類型。例如,可以將文本文件直接顯示在文本框或區域中,圖片文件使用 img 標簽顯示,音頻和視頻文件使用 audio 或 video 標簽顯示。通過在前端頁面上顯示文件流,可以在線預覽和查看文件內容。

FileReader 工作流程和事件觸發

  • 初始化 FileReader 對象:
const reader = new FileReader();
  • 設置 onload 事件處理程序:
reader.onload = function(event) {
  // 讀取操作成功完成時執行的代碼
  const result = event.target.result;
  console.log('文件內容:', result);
};
  • 調用讀取方法:
const file = ...; // 獲取的文件對象
reader.readAsArrayBuffer(file); // 或者使用其他讀取方法

當調用 readAsArrayBuffer, readAsDataURL 或 readAsText 方法時,FileReader 會開始讀取文件。當讀取操作成功完成后,onload 事件會被觸發,并且 FileReader 對象的 result 屬性包含了讀取到的數據。

事件順序

FileReader 觸發的事件按以下順序發生:

  1. onloadstart:讀取操作開始時觸發。
  2. onprogress:讀取過程中持續觸發,可以用于顯示進度信息。
  3. onload:讀取操作成功完成時觸發。
  4. onloadend:讀取操作完成(無論成功還是失敗)時觸發。
  5. onerror:讀取操作失敗時觸發。
  6. onabort:讀取操作被中止時觸發。

下面的示例代碼展示了如何在讀取文件時顯示讀取進度:

document.getElementById('fileInput').addEventListener('change', function(event) {
      const file = event.target.files[0];
      const reader = new FileReader();

      // 進度事件
      reader.onprogress = function(e) {
        if (e.lengthComputable) {
          const percentLoaded = (e.loaded / e.total) * 100;
          document.getElementById('progressBar').value = percentLoaded;
        }
      };

      // 定義 onload 事件處理程序
      reader.onload = function(e) {
        const content = e.target.result;
        document.getElementById('fileContent').textContent = content;
      };

      // 讀取文件為文本
      reader.readAsText(file);
    });

2. 文件分片

其實呢,無論是分片上傳和分片下載最核心的點就是需要對文件資源進行分片處理。

并且有很多現成的庫或者框架都會為我們來實現該部分,但是呢本著探索知識的本質,我們還是對其內部比較核心的部分做一次講解。

在前端范圍內,我們使用JavaScript中的File API[7]獲取文件對象,并使用Blob.prototype.slice()[8]方法將文件切成多個分片,從而實現分片上傳。

讓我們將第一節中的代碼在稍加改造。

改造readFileToArrayBuffer

/**
 * 將文件讀取為 ArrayBuffer 并分片
 * @param file 要讀取的文件
 * @returns 返回包含分片 Blob 數組的 Promise
 */
function readFileToArrayBuffer(file: File): Promise<{ chunkList: Blob[] }> {
  return new Promise((resolve, reject) => {
    let currentChunk = 0; // 當前分片的索引
    const chunkSize = 1024 * 1024; // 設置分片大小為 1MB
    const chunks = Math.ceil(file.size / chunkSize); // 計算總分片數
    const fileReader = new FileReader(); // 創建 FileReader 對象
    const chunkList: Blob[] = []; // 存儲分片的數組

    // 文件讀取完成后的回調函數
    fileReader.onload = function (e) {
      currentChunk++; // 增加當前分片索引

      // 如果還有分片需要讀取,繼續讀取下一個分片
      if (currentChunk < chunks) {
        loadNextChunk();
      } else {
        // 所有分片讀取完成,resolve Promise 并返回分片數組
        resolve({ chunkList });
      }
    };

    // 文件讀取出錯時的回調函數
    fileReader.onerror = function (e) {
      console.warn('讀取文件出錯', e);
      reject(e); // reject Promise 并傳遞錯誤信息
    };

    // 讀取下一個分片的函數
    function loadNextChunk() {
      const start = currentChunk * chunkSize; // 當前分片的起始字節
      const end = start + chunkSize >= file.size ? file.size : start + chunkSize; // 當前分片的結束字節

      const chunk = file.slice(start, end); // 切割文件得到當前分片
      chunkList.push(chunk); // 將當前分片添加到分片數組中
      fileReader.readAsArrayBuffer(chunk); // 讀取當前分片為 ArrayBuffer
    }

    // 開始讀取第一個分片
    loadNextChunk();
  });
}

?

當然,在進行文件上傳時,有時候需要用到md5加密等。計算文件的md5是為了檢查上傳到服務器的文件是否與用戶所傳的文件一致,由于行文限制,這里我們不做介紹。(其實在分片完成,就可以執行加密處理)

然后,我們就可以在readFileToArrayBuffer的調用處,獲取到對應文件的分片信息。

function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0]; // 獲取選擇的文件
    if (file) {
      readFileToArrayBuffer(file)
        .then(({ chunkList }) => {
          for (let i = 0; i < chunkList.length; i++) {
            const chunk = chunkList[i];
            console.log('chunk', chunk);
          }
        })
        .catch((error) => {
          console.error('文件讀取失敗:', error);
        });
    }
  }

然后,我們就可以在for循環中執行后續的操作了。

3. 分片上傳

大文件上傳可能會很慢、效率低并且不可靠,但有一些解決方案可以改善上傳過程的性能和穩定性。

傳統上傳 VS 分片上傳

傳統上傳方法的問題

分片上傳的優點

大文件上傳耗時長,容易導致超時。

將大文件拆分成較小的分片,更快更可靠地上傳。

占用服務器和網絡帶寬資源,可能影響其他用戶的訪問速度。

監控并顯示上傳進度,提高用戶體驗。

如果上傳中斷,需要重新上傳整個文件,效率低下。

充分利用瀏覽器的并發上傳能力,減輕服務器負載。

難以顯示和控制上傳進度。

實現斷點續傳功能,避免重新上傳已上傳的分片。

代碼實現

在前一節中,我們不是已經能夠獲取到chunklist信息了嗎。此時,我們就可以在for循環中執行上傳操作。

而實現前端分片上傳的主要步驟如下

  1. 通過FormData對象和AJAX或Fetch API[9]發送分片到服務器。
  2. 服務器接收分片并暫存,所有分片接收完成后合并為完整文件。
  3. 客戶端可以監聽上傳進度事件并在進度條或提示中顯示進度。

下面,我們主要講講前端范圍的邏輯實現。

readFileToArrayBuffer(file)
    .then(async ({ chunkList }) => {
      for (let i = 0; i < chunkList.length; i++) {
        const chunk = chunkList[i];
        await upChunk(chunk, i);
      }
    })
    .catch((error) => {
      console.error('文件讀取失敗:', error);
    });

我們將chunk上傳的邏輯,封裝成一個函數upChunk,其主要的邏輯如下:

/**
 * 異步上傳文件分片
 *
 * @param chunk - 當前需要上傳的文件分片 (Blob 對象)
 * @param index - 當前文件分片的索引
 */
const upChunk = async (chunk: Blob, index: number) => {
  const formData = new FormData();
  // 上傳的唯一標識符,用于區分不同的文件上傳,前后端約定的值
  formData.append('uploadId', 'front789');
  formData.append('partIndex', index.toString());
  formData.append('partFile', chunk);

  try {
    // 發送 POST 請求上傳當前分片
    await axios.post('上傳地址', formData, {
      headers: { 'Content-Type': 'multipart/form-data' },
      onUploadProgress: (progressEvent) => {
        // 檢查進度事件的總大小是否存在
        if (progressEvent.total) {
          // 計算已上傳的百分比
          const percentCompleted = Math.round((progressEvent.loaded / progressEvent.total) * 100);
          // 在這里添加更新進度條的邏輯
        }
      },
    });
  } catch (error) {
    // 如果上傳失敗,打印錯誤信息
    console.error(`Chunk ${index + 1} upload failed:`, error);
  }
  // 打印分片上傳完成的信息
  console.log(`上傳分片 ${index}完成`);
};

當我們把所有的chunklist都上傳成功后,后端服務會將上傳的分片組裝成完整的文件。

我們使用了axios_onUploadProgress[10]來處理文件上傳進度問題,然后我們可以在特定的位置改變一下state的值,這樣就可以實時顯示文檔上傳進度了。

4. 分片下載

傳統文件下載 VS 文件分片下載

文件分片下載是一種通過將大文件拆分成較小的片段(分片)并同時下載它們來提高文件下載效率的技術。

問題/技術

傳統文件下載

文件分片下載

長時間等待

用戶可能需要等待很長時間才能開始使用大文件

只需下載第一個分片,客戶端就可以開始使用文件

網絡擁堵

如果網絡帶寬被大文件下載占用,其他用戶可能會遇到下載速度慢的問題

可以使用多個并行請求來下載分片,充分利用帶寬并提高整體下載速度

難以恢復下載

如果網絡故障或用戶中斷,整個文件必須重新下載

如果下載被中斷,只需重新下載未完成的分片,而不是整個文件

下載效率

下載速度較慢,特別是在網絡不穩定或速度較慢的情況下

通過將大文件拆分成較小的片段并同時下載,提高文件下載效率

并行下載

不支持

支持,可以使用多個并行請求來下載分片

下載管理

整個文件作為一個整體進行下載

每個分片可以單獨管理和下載,提供更好的靈活性

分片下載的實現步驟

實現客戶端分片下載的基本解決方案如下:

  1. 服務器端將大文件切割成多個分片,并為每個分片生成唯一標識符。
  2. 客戶端發送請求以獲取分片列表并開始下載第一個分片。
  3. 在下載過程中,客戶端基于分片列表發起并發請求以下載其他分片,并逐漸拼接和合并下載的數據。
  4. 當所有分片下載完成后,客戶端將下載的數據合并為一個完整的文件。

示例代碼

async function downloadable() {
  try {
    // 發送文件下載請求,獲取文件的總大小和總分片數
    const response = await fetch('/download', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // 解析響應數據
    const data = await response.json();
    const totalSize = data.totalSize;
    const totalChunks = data.totalChunks;

    // 初始化變量
    let downloadedChunks = 0;
    const chunks: Blob[] = [];

    // 下載每個分片
    for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
      try {
        const chunkResponse = await fetch(`/download/${chunkNumber}`, {
          method: 'GET',
        });

        const chunk = await chunkResponse.blob();
        downloadedChunks++;
        chunks.push(chunk);

        // 當所有分片下載完成時
        if (downloadedChunks === totalChunks) {
          // 合并分片
          const mergedBlob = new Blob(chunks);

          // 創建對象 URL 以生成下載鏈接
          const downloadUrl = window.URL.createObjectURL(mergedBlob);

          // 創建一個 <a> 元素并設置屬性
          const link = document.createElement('a');
          link.href = downloadUrl;
          link.setAttribute('download', 'file.txt');

          // 模擬點擊下載
          link.click();

          // 釋放資源
          window.URL.revokeObjectURL(downloadUrl);
        }
      } catch (chunkError) {
        console.error(`Chunk ${chunkNumber} download failed:`, chunkError);
      }
    }
  } catch (error) {
    console.error('文件下載失敗:', error);
  }
}

我們先使用 Blob 對象創建一個總對象 URL,用于生成下載連接。然后創建一個標簽,并將 href 屬性設置為剛創建的對象 URL。繼續設置標簽的屬性以下載文件名,這樣在點擊時可以自動下載文件。

5. 斷點續傳

在前端,可以使用localStorage或sessionStorage存儲已上傳分片的信息,包括已上傳的分片索引和分片大小。

每次上傳前,檢查本地存儲中是否存在已上傳分片信息。如果存在,則從斷點處繼續上傳。

在后端,可以使用臨時文件夾或數據庫記錄已接收的分片信息,包括已上傳的分片索引和分片大小。

上傳完成前,保存上傳狀態,以便在上傳中斷時能夠恢復上傳進度。

import axios from 'axios';
import React, { useState, useEffect, ChangeEvent } from 'react';

function FileUp() {
  const [file, setFile] = useState(null); // 本地上傳的文件
  const [uploadedChunks, setUploadedChunks] = useState([]); // 已上傳的分片列表
  const [uploading, setUploading] = useState(false); // 上傳是否進行中

  function handleFileChange(event: ChangeEvent<HTMLInputElement>) {
    setFile(event.target.files?.[0]);
  }

  // 處理文件選擇事件
  async function upload() {
    if (!file) {
      alert('請選擇要上傳的文件!');
      return;
    }
    const chunkSize = 1024 * 1024; // 1MB
    const totalChunks = Math.ceil(file.size / chunkSize);

    let start = 0;
    let end = Math.min(chunkSize, file.size);
    setUploading(true);

    for (let i = 0; i < totalChunks; i++) {
      const chunk = file.slice(start, end);
      const uploadedChunkIndex = uploadedChunks.indexOf(i);

      if (uploadedChunkIndex === -1) {
        try {
          const response = await upChunk(chunk, i);
          setUploadedChunks((prevChunks) => [...prevChunks, i]);

          // 將已上傳的分片列表保存到本地存儲
          localStorage.setItem('uploadedChunks', JSON.stringify(uploadedChunks));
        } catch (error) {
          console.error(error); // 處理錯誤
        }
      }

      start = end;
      end = Math.min(start + chunkSize, file.size);
    }

    setUploading(false);

    // 上傳完成,清除本地存儲中的分片信息
    localStorage.removeItem('uploadedChunks');
  }

  const upChunk = async (chunk: Blob, index: number) => {
    const formData = new FormData();
    // 這應該是一個隨機值,用于標識當前上傳的文件,這是和后端做約定的值
    formData.append('uploadId', 'front789');
    formData.append('partIndex', index.toString());
    formData.append('partFile', chunk);

    try {
      return await axios.post(`https://Front789/api/uploadChunk`, formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
      });
    } catch (error) {
      console.error(`Chunk ${index + 1} upload failed:`, error);
    }

    console.log(`上傳分片 ${index}完成`);
  };

  useEffect(() => {
    const storedUploadedChunks = localStorage.getItem('uploadedChunks');

    if (storedUploadedChunks) {
      setUploadedChunks(JSON.parse(storedUploadedChunks));
    }
  }, []);

  return (
    <div>
      <input type="file" onChange={handleFileChange} />
      <button onClick={upload} disabled={uploading}>
        {uploading ? `上傳中..` : '上傳'}
      </button>
    </div>
  );
}

export default FileUp;

該FileUp函數組件使用React的useState鉤子創建uploadedChunks狀態來保存已上傳的分片索引數組。

當用戶選擇要上傳的文件時,handleFileChange()函數會更file狀態。

upChunk()函數將分片發送到服務器并返回一個Promise對象來處理響應。

upload()函數通過獲取總分片數并將uploading狀態設置為true來禁用上傳按鈕,從斷點處繼續上傳。它遍歷所有分片并檢查分片索引是否已包含在uploadedChunks數組中。如果沒有,該函數會上傳分片并將已上傳的分片索引添加到uploadedChunks數組中。然后使用localStorage保存已上傳的分片信息。最后,上傳完成后,函數會將uploading狀態設置為false并清除本地存儲中的分片信息。

在上傳大文件時,需要考慮服務器的處理能力和存儲空間,以及安全問題。同時,避免并發上傳相同文件以確保續傳的準確性。可以使用唯一的文件標識符或用戶會話標識符來區分。

責任編輯:武曉燕 來源: 前端柒八九
相關推薦

2021-01-15 11:40:44

文件Java秒傳

2022-06-15 09:01:45

大文件秒傳分片上傳

2025-07-03 07:41:34

2021-01-18 05:19:11

數字指紋

2024-11-12 09:54:23

2025-03-28 05:10:00

Spring上傳大文件

2013-03-22 14:42:01

OSS開放存儲服務云計算

2017-04-01 17:30:36

MongoDB分片實現

2019-11-12 09:32:39

分布式elastic-job分片

2009-11-16 11:41:19

PHP上傳大文件

2022-06-13 14:06:33

大文件上傳前端

2015-08-07 15:35:42

ios短點下載源碼

2013-11-28 09:48:55

MongoDBSharding分片

2022-05-09 13:36:27

加密貨幣區塊鏈區塊鏈分片

2013-11-25 10:45:04

MongoDB

2019-02-19 10:12:41

Redis分片數據

2025-07-02 00:00:00

2009-07-21 15:38:31

2025-08-29 01:11:00

2024-12-20 12:12:19

Redis負載均衡節點
點贊
收藏

51CTO技術棧公眾號

r级无码视频在线观看| 亚洲综合精品伊人久久| 熟女少妇一区二区三区| 欧美极品免费| 国产精品久线在线观看| 亚洲精品欧美极品| 欧美三级一区二区三区| 国产欧美日韩在线一区二区| 欧美日韩www| 喜爱夜蒲2在线| 日中文字幕在线| 久久丁香综合五月国产三级网站| 欧美激情xxxx| 国产真人真事毛片视频| 成人午夜大片| 欧美日韩国产不卡| 九一国产精品视频| 日本在线免费看| 成人h动漫精品一区二区| 国产精品爱久久久久久久| 内射一区二区三区| 亚洲人成网77777色在线播放| 欧美美女直播网站| 各处沟厕大尺度偷拍女厕嘘嘘 | 亚洲高清久久网| 日本熟妇人妻中出| 精灵使的剑舞无删减版在线观看| 国产亚洲一区二区三区| 99一区二区| 亚洲中文无码av在线| 激情综合中文娱乐网| 中文字幕在线日韩| 青青草视频成人| 亚洲一区二区三区免费| 欧美日韩三级视频| 日本一本二本在线观看| 男人添女人下部高潮视频在线观看| 日本一区二区视频在线| 久久精品一区二区三区不卡免费视频| 97精品久久人人爽人人爽| 亚洲一区激情| 国模极品一区二区三区| 日韩在线中文字幕视频| 色爱综合网欧美| 亚洲美女中文字幕| 国产婷婷在线观看| 91久久偷偷做嫩草影院电| 欧美日韩国产美女| 亚欧在线免费观看| 日韩欧美看国产| 欧美三级xxx| 国产免费黄色一级片| www中文字幕在线观看| 亚洲天堂中文字幕| 亚洲精品一区二区三区樱花| 黄色片在线播放| 337p粉嫩大胆噜噜噜噜噜91av | 91亚洲国产成人久久精品麻豆| 亚洲综合丁香| 18性欧美xxxⅹ性满足| 国产在线拍揄自揄拍| 激情视频一区| 国内精品伊人久久| 免费在线观看黄网站| 在线高清一区| 午夜精品视频在线| 欧美一级视频免费观看| 在线午夜精品| 日本久久久久久久| 中文字幕+乱码+中文| 蜜臀精品久久久久久蜜臀| 国产精品视频yy9099| 最好看的日本字幕mv视频大全| 日韩中文字幕亚洲一区二区va在线 | 欧美日韩免费| 久久久久久91| 色婷婷在线观看视频| 亚洲一区欧美激情| 国产精品88a∨| 91中文字幕在线视频| 国产麻豆视频一区| 国产精品三区在线| 免费一区二区三区在在线视频| 激情综合网俺也去| www.成人在线视频| 337p亚洲精品色噜噜狠狠| 极品人妻一区二区| 欧美巨大xxxx| 正在播放国产一区| 印度午夜性春猛xxx交| 在线精品亚洲| 国产成人avxxxxx在线看| 在线观看毛片av| 国产高清视频一区| 欧美极品一区二区| 毛片在线看网站| 亚洲成人在线免费| 亚洲欧美另类动漫| 一区二区三区四区高清视频| 日韩国产精品一区| 国产又粗又长又硬| 亚洲区欧美区| 国产欧美久久久久久| 色婷婷视频在线| 国产精品三级视频| 国产二区视频在线| 欧美午夜三级| 亚洲精品国产精品乱码不99按摩 | 久久精品欧美一区二区三区不卡| 亚洲伊人婷婷| 日本不卡1234视频| 在线播放中文字幕一区| 亚洲av成人片色在线观看高潮| 日韩精品久久| 97精品伊人久久久大香线蕉| 国产又粗又猛又黄| 91一区二区在线观看| 欧美另类videosbestsex日本| 欧洲一级精品| 欧美精品一区二区蜜臀亚洲| 日韩精品一区二区三区在线视频| 国产欧美在线| 99超碰麻豆| 色三级在线观看| 色成年激情久久综合| 中文视频在线观看| 欧美激情视频一区二区三区在线播放| 欧美在线不卡区| 亚洲国产视频一区二区三区| 国产精品美女久久福利网站| 37pao成人国产永久免费视频| 日韩激情综合| www日韩欧美| 欧美在线视频精品| 久久综合久久99| 自拍日韩亚洲一区在线| 亚洲欧美日本国产| 美女扒开尿口让男人操亚洲视频网站 | 99国产**精品****| 日韩美女免费视频| 亚洲av成人无码网天堂| 亚洲国产综合色| 日本少妇一区二区三区| 婷婷亚洲最大| 国产免费成人av| 成人欧美一区| 在线观看91精品国产入口| 一本加勒比波多野结衣| 亚洲精品乱码| 精品久久蜜桃| 国产ktv在线视频| 亚洲国产精品va在线观看黑人| 久久久久香蕉视频| 国产99精品在线观看| 波多野结衣av一区二区全免费观看| 一区二区三区日本视频| 日韩在线一区二区三区免费视频| 中文字幕第99页| 国产欧美日韩一区二区三区在线观看 | 粉嫩一区二区三区国产精品| 在线欧美日韩国产| 最新日韩免费视频| 激情都市一区二区| 蜜臀av性久久久久蜜臀av| 视频精品一区二区三区| 欧美国产日韩xxxxx| 刘玥91精选国产在线观看| 午夜免费久久看| 少妇精品一区二区三区| 日韩激情一区二区| 亚洲巨乳在线观看| 久久gogo国模啪啪裸体| 欧美精品福利视频| 日本一级在线观看| 91国模大尺度私拍在线视频| 久久嫩草捆绑紧缚| 国产成人久久精品77777最新版本 国产成人鲁色资源国产91色综 | 日韩影视在线观看| 国产精品吊钟奶在线| 高h视频在线观看| 精品国产乱码久久久久久蜜臀| 国产尤物在线视频| 国产欧美日韩久久| 99精品视频免费版的特色功能| 在线成人www免费观看视频| 欧美大香线蕉线伊人久久| 日韩一区二区三区免费| 欧美精品在线极品| 色网站在线免费观看| 欧美精品高清视频| 日本亚洲欧美在线| 日本一区二区三区在线不卡 | 97久久精品人人爽人人爽蜜臀| 久久久国产欧美| 欧美天天视频| 欧美日韩另类综合| 国产精品99久久免费| 88xx成人精品| av毛片在线免费| 亚洲视频在线观看| 亚洲成人精品女人久久久| 一本色道**综合亚洲精品蜜桃冫| 中文字幕在线观看2018| 91在线视频播放地址| 在线免费看污网站| 性欧美videos另类喷潮| 中文字幕av日韩精品| 日本一区福利在线| 91丨九色丨国产在线| 成人美女视频| 久久久女女女女999久久| 91精品国产综合久久久久久豆腐| 亚洲国产精品福利| 国产精品久久久久久久成人午夜| 精品福利视频导航| 国产精品久久久久久久精| 久久久精品人体av艺术| 黄色av电影网站| 狠狠色伊人亚洲综合成人| 国产精品免费成人| 中国女人久久久| 97av中文字幕| 婷婷中文字幕一区| 亚洲自拍偷拍二区| 伊人春色精品| 国产伦精品一区二区三区免| 国语精品视频| 国产日韩欧美在线| 外国电影一区二区| 国产v综合ⅴ日韩v欧美大片| 182在线视频观看| 九色成人免费视频| 久操视频在线观看| 精品久久久av| 亚洲s色大片| 最新国产成人av网站网址麻豆| 欧美日韩在线中文字幕| 亚洲精品久久视频| 亚洲精品福利网站| 精品久久久久一区| 懂色av一区二区三区四区 | 五月婷婷丁香花| 精品国产麻豆免费人成网站| 亚洲va欧美va| 日韩精品一区二区三区视频播放 | 亚洲白虎美女被爆操| 精品乱子伦一区二区| 日韩视频免费观看高清完整版在线观看| 中文字幕第一页在线播放| 欧美性受xxxx| 怡红院男人天堂| 欧美狂野另类xxxxoooo| 国产精品久久久国产盗摄| 6080日韩午夜伦伦午夜伦| 国产裸体无遮挡| 欧美一区二区三区影视| av中文字幕第一页| 欧美成人伊人久久综合网| 午夜精品一区二区三| 精品国产3级a| 无码国产色欲xxxx视频| 日韩电影中文字幕一区| 欧美少妇另类| 丝袜情趣国产精品| 黄网站免费在线播放| 久久成人人人人精品欧| 性xxxfreexxxx性欧美| 久久久久在线观看| a欧美人片人妖| 国产精品视频网址| www.成人在线.com| 国产精品永久入口久久久| 亚洲va久久久噜噜噜久久| 欧美日韩在线精品一区二区三区| 欧洲杯足球赛直播| 男女爱爱视频网站| 亚洲国产美女| 日本三区在线观看| 久久99国产精品久久99果冻传媒| 中文字幕乱码在线人视频| 99久久久无码国产精品| 337人体粉嫩噜噜噜| 亚洲欧美日韩精品久久久久| www.av麻豆| 欧美日韩在线精品一区二区三区激情| 国产www视频| 日韩福利视频在线观看| 日韩欧美小视频| 欧美激情国产高清| 欧美日韩尤物久久| 99视频国产精品免费观看| 国产videos久久| 欧美日韩在线视频一区二区| 中文字幕乱码人妻综合二区三区| 免费精品99久久国产综合精品| 亚洲男人天堂2021| 97se亚洲国产综合自在线观| 刘亦菲国产毛片bd| 午夜精品久久久久久久99樱桃| 人人妻人人爽人人澡人人精品| 91精品国产91久久综合桃花| 天天干视频在线| 日韩日本欧美亚洲| 美女搞黄视频在线观看| 成人激情综合网| 伊人久久大香线蕉综合网蜜芽| 最近看过的日韩成人| 亚洲永久字幕| 深夜做爰性大片蜜桃| 国产亚洲1区2区3区| 国产精品1234区| 欧美丰满高潮xxxx喷水动漫| 欧美精品少妇| 美日韩精品视频免费看| 日韩三区免费| 蜜桃av久久久亚洲精品| 欧美精品三区| 亚洲综合婷婷久久| 91麻豆免费在线观看| 中文字幕在线有码| 欧美视频在线播放| 香蕉视频免费在线看| 欧美风情在线观看| 国产一区2区在线观看| 日韩精品一区二区三区外面| 日韩视频在线一区二区三区| 人妻精油按摩bd高清中文字幕| 中文字幕成人在线观看| 日本中文字幕第一页| 亚洲精品电影网站| av小说在线播放| 91高跟黑色丝袜呻吟在线观看| 99精品视频在线观看播放| 在线免费av播放| 国产午夜亚洲精品午夜鲁丝片| 好吊妞视频一区二区三区| 亚洲国产精品va| 大香伊人久久| 国精产品99永久一区一区| 黄色av一区| 国产乱淫av麻豆国产免费| 亚洲精品自拍动漫在线| 国产欧美日韩成人| 日韩中文字幕国产精品| 狂野欧美性猛交xxxx| 日韩一区二区电影在线观看| 日韩av一二三| 国产精品1区2区3区4区| 欧美色倩网站大全免费| www.在线视频.com| 国产精品自拍网| 日韩在线观看| 色噜噜狠狠一区二区三区狼国成人| 国产精品免费av| 国产精品久久久久久久久久久久久久久久久久 | 国产亚洲欧美日韩在线一区| 国产午夜免费福利| 亚洲美女福利视频网站| 欧美福利在线播放| 亚洲精品无人区| 久久99精品视频| 草视频在线观看| 欧美va在线播放| а√天堂8资源在线| 久久久久久九九| 久久激情综合| 香蕉久久久久久久| 日韩精品一区在线观看| japanese色国产在线看视频| 久久免费99精品久久久久久| 久久亚洲欧洲| 日本黄区免费视频观看| 欧美一区二区三区影视| 国产网红在线观看| 麻豆亚洲一区| 精品在线观看免费| 九九九免费视频| 精品无码久久久久久国产| 另类中文字幕国产精品| 国产树林野战在线播放| av资源站一区| 中国女人真人一级毛片| 色综合色综合久久综合频道88| 琪琪久久久久日韩精品| 日本免费观看网站| 亚洲精品写真福利| 午夜影院在线视频| 国产精品一区久久久| 黄色亚洲在线| 亚洲黄色网址大全| 亚洲成人动漫在线播放| 午夜精品久久久久久久久久蜜桃| 一区中文字幕在线观看| 成人激情小说网站| 中文字幕网址在线| 国内精品久久久| 色综合天天爱| 国产精品第七页| 91精品国产91久久久久久一区二区| 国产99在线观看|