日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當前位置:首頁 > 科技  > 軟件

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

來源: 責編: 時間:2024-07-02 17:37:18 146觀看
導讀1. 文件流操作在軟件開發中,我們會看到各種形形色色的文件/資源(pdf/word/音頻/視頻),其實它們歸根到底就是不同數據格式的以滿足自身規則的情況下展示。說的更淺顯易懂點,它們都是數據,并且最終都會以二進制形式展示。也

1. 文件流操作

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

在前端開發中,文件流操作允許我們通過數據流來處理文件,執行諸如讀取、寫入和刪除文件的操作。hIz28資訊網——每日最新資訊28at.com

在前端開發中,文件可以作為數據流來處理。數據流是從一個源到另一個目的地傳輸的數據序列。hIz28資訊網——每日最新資訊28at.com

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

在前端處理二進制數據時,有兩個對象是繞不開的。hIz28資訊網——每日最新資訊28at.com

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

使用 FileReader 讀取文件

FileReader 是一個前端瀏覽器 API,允許我們異步讀取文件內容并將其轉換為可用的數據格式,如文本或二進制數據。hIz28資訊網——每日最新資訊28at.com

它提供了如 readAsText()[4] 和 readAsArrayBuffer()[5] 等方法,可以根據我們的需要進行選擇。hIz28資訊網——每日最新資訊28at.com

圖片圖片hIz28資訊網——每日最新資訊28at.com

使用案例

下面,我們來用一個例子來簡單說明一下FileReader的使用方式。hIz28資訊網——每日最新資訊28at.com

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信息了。hIz28資訊網——每日最新資訊28at.com

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

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

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 屬性包含了讀取到的數據。hIz28資訊網——每日最新資訊28at.com

事件順序

FileReader 觸發的事件按以下順序發生:hIz28資訊網——每日最新資訊28at.com

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

下面的示例代碼展示了如何在讀取文件時顯示讀取進度:hIz28資訊網——每日最新資訊28at.com

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. 文件分片

其實呢,無論是分片上傳和分片下載最核心的點就是需要對文件資源進行分片處理。hIz28資訊網——每日最新資訊28at.com

并且有很多現成的庫或者框架都會為我們來實現該部分,但是呢本著探索知識的本質,我們還是對其內部比較核心的部分做一次講解。hIz28資訊網——每日最新資訊28at.com

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

讓我們將第一節中的代碼在稍加改造。hIz28資訊網——每日最新資訊28at.com

改造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();  });}

?hIz28資訊網——每日最新資訊28at.com

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

然后,我們就可以在readFileToArrayBuffer的調用處,獲取到對應文件的分片信息。hIz28資訊網——每日最新資訊28at.com

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循環中執行后續的操作了。hIz28資訊網——每日最新資訊28at.com

3. 分片上傳

大文件上傳可能會很慢、效率低并且不可靠,但有一些解決方案可以改善上傳過程的性能和穩定性。hIz28資訊網——每日最新資訊28at.com

傳統上傳 VS 分片上傳

傳統上傳方法的問題
hIz28資訊網——每日最新資訊28at.com

分片上傳的優點
hIz28資訊網——每日最新資訊28at.com

大文件上傳耗時長,容易導致超時。
hIz28資訊網——每日最新資訊28at.com

將大文件拆分成較小的分片,更快更可靠地上傳。
hIz28資訊網——每日最新資訊28at.com

占用服務器和網絡帶寬資源,可能影響其他用戶的訪問速度。
hIz28資訊網——每日最新資訊28at.com

監控并顯示上傳進度,提高用戶體驗。
hIz28資訊網——每日最新資訊28at.com

如果上傳中斷,需要重新上傳整個文件,效率低下。
hIz28資訊網——每日最新資訊28at.com

充分利用瀏覽器的并發上傳能力,減輕服務器負載。
hIz28資訊網——每日最新資訊28at.com

難以顯示和控制上傳進度。
hIz28資訊網——每日最新資訊28at.com

實現斷點續傳功能,避免重新上傳已上傳的分片。
hIz28資訊網——每日最新資訊28at.com

代碼實現

在前一節中,我們不是已經能夠獲取到chunklist信息了嗎。此時,我們就可以在for循環中執行上傳操作。hIz28資訊網——每日最新資訊28at.com

而實現前端分片上傳的主要步驟如下hIz28資訊網——每日最新資訊28at.com

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

下面,我們主要講講前端范圍的邏輯實現。hIz28資訊網——每日最新資訊28at.com

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,其主要的邏輯如下:hIz28資訊網——每日最新資訊28at.com

/** * 異步上傳文件分片 * * @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都上傳成功后,后端服務會將上傳的分片組裝成完整的文件。hIz28資訊網——每日最新資訊28at.com

我們使用了axios_onUploadProgress[10]來處理文件上傳進度問題,然后我們可以在特定的位置改變一下state的值,這樣就可以實時顯示文檔上傳進度了。hIz28資訊網——每日最新資訊28at.com

4. 分片下載

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

文件分片下載是一種通過將大文件拆分成較小的片段(分片)并同時下載它們來提高文件下載效率的技術。hIz28資訊網——每日最新資訊28at.com

問題/技術hIz28資訊網——每日最新資訊28at.com

傳統文件下載hIz28資訊網——每日最新資訊28at.com

文件分片下載hIz28資訊網——每日最新資訊28at.com

長時間等待hIz28資訊網——每日最新資訊28at.com

用戶可能需要等待很長時間才能開始使用大文件
hIz28資訊網——每日最新資訊28at.com

只需下載第一個分片,客戶端就可以開始使用文件
hIz28資訊網——每日最新資訊28at.com

網絡擁堵hIz28資訊網——每日最新資訊28at.com

如果網絡帶寬被大文件下載占用,其他用戶可能會遇到下載速度慢的問題
hIz28資訊網——每日最新資訊28at.com

可以使用多個并行請求來下載分片,充分利用帶寬并提高整體下載速度
hIz28資訊網——每日最新資訊28at.com

難以恢復下載hIz28資訊網——每日最新資訊28at.com

如果網絡故障或用戶中斷,整個文件必須重新下載
hIz28資訊網——每日最新資訊28at.com

如果下載被中斷,只需重新下載未完成的分片,而不是整個文件
hIz28資訊網——每日最新資訊28at.com

下載效率hIz28資訊網——每日最新資訊28at.com

下載速度較慢,特別是在網絡不穩定或速度較慢的情況下
hIz28資訊網——每日最新資訊28at.com

通過將大文件拆分成較小的片段并同時下載,提高文件下載效率
hIz28資訊網——每日最新資訊28at.com

并行下載hIz28資訊網——每日最新資訊28at.com

不支持
hIz28資訊網——每日最新資訊28at.com

支持,可以使用多個并行請求來下載分片
hIz28資訊網——每日最新資訊28at.com

下載管理hIz28資訊網——每日最新資訊28at.com

整個文件作為一個整體進行下載
hIz28資訊網——每日最新資訊28at.com

每個分片可以單獨管理和下載,提供更好的靈活性
hIz28資訊網——每日最新資訊28at.com

分片下載的實現步驟

實現客戶端分片下載的基本解決方案如下:hIz28資訊網——每日最新資訊28at.com

  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。繼續設置標簽的屬性以下載文件名,這樣在點擊時可以自動下載文件。hIz28資訊網——每日最新資訊28at.com

5. 斷點續傳

在前端,可以使用localStorage或sessionStorage存儲已上傳分片的信息,包括已上傳的分片索引和分片大小。hIz28資訊網——每日最新資訊28at.com

每次上傳前,檢查本地存儲中是否存在已上傳分片信息。如果存在,則從斷點處繼續上傳。hIz28資訊網——每日最新資訊28at.com

在后端,可以使用臨時文件夾或數據庫記錄已接收的分片信息,包括已上傳的分片索引和分片大小。hIz28資訊網——每日最新資訊28at.com

上傳完成前,保存上傳狀態,以便在上傳中斷時能夠恢復上傳進度。hIz28資訊網——每日最新資訊28at.com

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狀態來保存已上傳的分片索引數組。hIz28資訊網——每日最新資訊28at.com

當用戶選擇要上傳的文件時,handleFileChange()函數會更file狀態。hIz28資訊網——每日最新資訊28at.com

upChunk()函數將分片發送到服務器并返回一個Promise對象來處理響應。hIz28資訊網——每日最新資訊28at.com

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

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

本文鏈接:http://www.www897cc.com/showinfo-26-98194-0.html聊聊大文件分片上傳和分片下載

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: ThinkPHP8框架集成Swoole實現高性能RPC服務

下一篇: 如何使用 React Query 做下拉數據自動刷新?

標簽:
  • 熱門焦點
  • 對標蘋果的靈動島 華為帶來實況窗功能

    繼蘋果的靈動島之后,華為也在今天正式推出了“實況窗”功能。據今天鴻蒙OS 4.0的現場演示顯示,華為的實況窗可以更高效的展現出實時通知,比如鎖屏上就能看到外賣、打車、銀行
  • Raft算法:保障分布式系統共識的穩健之道

    1. 什么是Raft算法?Raft 是英文”Reliable、Replicated、Redundant、And Fault-Tolerant”(“可靠、可復制、可冗余、可容錯”)的首字母縮寫。Raft算法是一種用于在分布式系統
  • 三言兩語說透柯里化和反柯里化

    JavaScript中的柯里化(Currying)和反柯里化(Uncurrying)是兩種很有用的技術,可以幫助我們寫出更加優雅、泛用的函數。本文將首先介紹柯里化和反柯里化的概念、實現原理和應用
  • WebRTC.Net庫開發進階,教你實現屏幕共享和多路復用!

    WebRTC.Net庫:讓你的應用更親民友好,實現視頻通話無痛接入! 除了基本用法外,還有一些進階用法可以更好地利用該庫。自定義 STUN/TURN 服務器配置WebRTC.Net 默認使用 Google 的
  • 慕巖炮轟抖音,百合網今何在?

    來源:價值研究所 作者:Hernanderz&ldquo;難道就因為自己的一個產品牛逼了,從客服到總裁,都不愿意正視自己產品和運營上的問題,選擇逃避了嗎?&rdquo;這一番話,出自百合網聯合創
  • 消費結構調整丨巨頭低價博弈,拼多多還卷得動嗎?

    來源:征探財經作者:陳香羽隨著流量紅利的退潮,電商的存量博弈越來越明顯。曾經主攻中高端與品質的淘寶天貓、京東重拾&ldquo;低價&rdquo;口號。而過去與他們錯位競爭的拼多多,靠
  • 當家的盒馬,加速謀生

    來源 | 價值星球Planet作者 | 歸去來自己&ldquo;當家&rdquo;的盒馬,開始加速謀生了。據盒馬官微消息,盒馬計劃今年開放生鮮供應鏈,將其生鮮商品送往食堂。目前,盒馬在上海已經與
  • 三星折疊屏手機去年銷售近1000萬臺 今年目標定為1500萬

    7月29日消息,三星率先發力可折疊手機市場,在全球市場已經取得了非常亮眼的成績,接下來會進一步鞏固和擴大這一優勢。三星在推出Galaxy Z Flip5和Galax
  • 榮耀Magicbook V 14 2021曙光藍版本正式開售,擁有觸摸屏

    榮耀 Magicbook V 14 2021 曙光藍版本正式開售,搭載 i7-11390H 處理器與 MX450 顯卡,配備 16GB 內存與 512GB SSD,重 1.48kg,厚 14.5mm,具有 1.5mm 鍵盤鍵程、
Top 主站蜘蛛池模板: 云林县| 喜德县| 临沂市| 伊川县| 清镇市| 太湖县| 黄浦区| 阜南县| 宜黄县| 龙井市| 伊金霍洛旗| 徐汇区| 镇雄县| 铜陵市| 镇安县| 桐乡市| 平凉市| 肇源县| 剑川县| 大同市| 马关县| 泊头市| 海伦市| 新宁县| 陕西省| 疏勒县| 清苑县| 龙井市| 南汇区| 开远市| 喜德县| 长岛县| 长沙市| 日照市| 灌南县| 乌拉特前旗| 旬阳县| 怀集县| 于都县| 浦东新区| 桑日县|