惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Hugging Face - Blog
Hugging Face - Blog
云风的 BLOG
云风的 BLOG
大猫的无限游戏
大猫的无限游戏
M
MIT News - Artificial intelligence
L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Recent Announcements
Recent Announcements
IT之家
IT之家
Google DeepMind News
Google DeepMind News
罗磊的独立博客
爱范儿
爱范儿
Last Week in AI
Last Week in AI
人人都是产品经理
人人都是产品经理
U
Unit 42
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
B
Blog
博客园 - 叶小钗
月光博客
月光博客
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
C
Check Point Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知

大佬论坛

52161.com 六一特价 500 易名 天涯这波是小作坊捞钱的吗,感觉很不专业的样子,居然连cdn都不用! 新天涯社区,卡爆了进不去啊! 20万拍到的域名,现在能涨到200万了吧 慢讯 关于天涯社区恢复访问进展的情况说明 新米 Gooey.cn 单词域名 1万左右收单字符域名,最好带V或者S,后缀最多不超过2位数 who.cx 现在已经不如 hu.is 好用了,这是怎么了? 最后几个四字母了,1588一个任选~ AI 做的图床真好看 用Discourse做论坛快到半个月了 兄弟们,我刚注册了域名 yin.hu 阴户 怎么样? 腾讯周年是啥时候 岛链壁纸主题开源 观星步入测试阶段 618和双十一哪个更火爆 nodebb.cc flarum.cc 出两个CC 天涯论坛明天回归了我感觉有压力 真正的天涯即将回归 你一定会用到的网站-WOCAONI.MA 上线 终于想到域名能做什么项目了。不容易啊。 沐雨云襄阳云服务器,性价比拉满 新米cdpc.cc niubi.ren 收一千五以内的单字符有木有 全国入户调查明天开始。。。 快快快!!Colocrossing洛杉矶补货补货了特供3c4g款20刀/年小鸡! 【开源自荐】耗费了几百亿token,我写了一个可以在浏览器运行的仿安卓系统 组合米,米表+交易站,打包888 5月满签记录记录!!! 小米御7 miyu7.com 只需288
[PHP] 文件二进制分片切割加密传输 - 大佬论坛
希米 · 2026-06-20 · via 大佬论坛

说明:

1.  前端将文件分片加密后上传,后端存储加密数据和索引

2. 下载时前端获取索引,逐片下载解密并合并成原文件

3. 加密方式是 AES-256 ,调用Crypto库前端执行的;

4.就一个演示案例,没有写后台,就两个文件,前端html,后端php;

5.文件上传在“uploads”目录内,可以打开php文件自己修改;

6.主要用途就是减少网络波动导致文件传输不稳定,与服务器上传文件大小限制;

7.传输实时显示进度条,使用的是4线程并发传输,局域网测试可以跑满带宽;

代码:

index.html

<!-- 
   Title:文件二进制分片加密传输
    Blog:https://www.ximi.me
    Author:希米
    Time:2036-06-19
    说明:
      - 前端将文件分片加密后上传,后端存储加密数据和索引
      - 下载时前端获取索引,逐片下载解密并合并成原文件
-->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文件二进制分片加密传输</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js"></script>
</head>

<body class="bg-gray-100 p-6">

<div class="max-w-2xl mx-auto space-y-6">

<h1 class="text-2xl font-bold text-center"> 文件二进制分片加密传输演示</h1>

<!-- 密钥 -->
<div class="bg-white p-4 rounded-xl">
  <input id="key" type="password" placeholder="加密密钥"
    class="w-full p-3 border rounded">
</div>

<!-- 上传 -->
<div class="bg-white p-4 rounded-xl">
  <h2 class="font-bold mb-2">上传文件</h2>

  <input type="file" id="file" class="mb-3">

  <button onclick="upload()" class="bg-blue-500 text-white px-4 py-2 rounded">
    开始上传
  </button>

  <div id="upStatus" class="text-sm mt-2 text-gray-600"></div>
  <progress id="upProgress" value="0" max="100" class="w-full mt-2"></progress>
</div>

<!-- 下载 -->
<div class="bg-white p-4 rounded-xl">
  <h2 class="font-bold mb-2">下载文件</h2>

  <input id="fid" placeholder="文件ID" class="w-full p-2 border mb-3">

  <button onclick="downloadFile()" class="bg-purple-500 text-white px-4 py-2 rounded">
    开始下载
  </button>

  <div id="downStatus" class="text-sm mt-2 text-gray-600"></div>
  <progress id="downProgress" value="0" max="100" class="w-full mt-2"></progress>
</div>

</div>

<script>


// ================= 工具 =================
function sha256(str){
  return CryptoJS.SHA256(str).toString();
}

function safeJSON(t){
  try{
    return JSON.parse(t);
  }catch(e){
    console.log("❌ 后端返回:", t);
    throw new Error("JSON解析失败");
  }
}

// ================= 并发池 =================
async function asyncPool(limit, arr, fn){
  const ret = []
  const executing = []

  for(const item of arr){
    const p = Promise.resolve().then(()=>fn(item))
    ret.push(p)

    const e = p.then(()=>executing.splice(executing.indexOf(e),1))
    executing.push(e)

    if(executing.length >= limit){
      await Promise.race(executing)
    }
  }

  return Promise.all(ret)
}

// ================= 上传(并行版) =================
async function upload(){

  const file = document.getElementById('file').files[0]
  const key = document.getElementById('key').value

  if(!file || !key) return alert("缺文件或密钥")

  const status = document.getElementById('upStatus')
  const bar = document.getElementById('upProgress')

  const buf = await file.arrayBuffer()
  const chunkSize = 256 * 1024

  const chunks = []
  const originalName = file.name

  // ================= 切块 =================
  for(let i=0;i<buf.byteLength;i+=chunkSize){

    const slice = buf.slice(i, i+chunkSize)

    const word = CryptoJS.lib.WordArray.create(slice)

    const encrypted = CryptoJS.AES.encrypt(word, key).toString()

    const name = Math.random().toString(36).slice(2) + ".bin"

    chunks.push({
      index: i,
      name,
      data: encrypted,
      hash: sha256(encrypted)
    })
  }

  const fileId = "f_" + Date.now()

  let done = 0
  const start = Date.now()

  status.innerText = "并行上传中..."

  // ================= 并行上传(核心) =================
  await asyncPool(4, chunks, async (c)=>{

    const fd = new FormData()
    fd.append("id", fileId)
    fd.append("name", c.name)
    fd.append("chunk", c.data)

    const res = await fetch("file.php?action=upload",{
      method:"POST",
      body:fd
    })

    const text = await res.text()
    const data = safeJSON(text)

    if(data.status !== "ok"){
      throw new Error("upload fail")
    }

    done++
    bar.value = (done / chunks.length) * 100

    let speed = (done / ((Date.now()-start)/1000)).toFixed(2)

    status.innerText = `上传 ${done}/${chunks.length} 速度:${speed}/s`
  })

  // ================= manifest =================
  const manifest = {
    originalName,
    chunks: chunks.map(c=>({
      name:c.name,
      hash:c.hash
    }))
  }

  const fd2 = new FormData()
  fd2.append("id", fileId)
  fd2.append("manifest", JSON.stringify(manifest))

  await fetch("file.php?action=upload",{
    method:"POST",
    body:fd2
  })

  bar.value = 100
  status.innerText = "上传完成 ID: " + fileId
}

// ================= 下载(并行版) =================
async function downloadFile(){

  const id = document.getElementById('fid').value
  const key = document.getElementById('key').value

  const status = document.getElementById('downStatus')
  const bar = document.getElementById('downProgress')

  const res = await fetch("file.php?action=send&id="+id)
  const text = await res.text()

  const data = safeJSON(text)

  if(data.status !== "ok"){
    throw new Error(data.msg)
  }

  const manifest = data.manifest
  const list = manifest.chunks
  const originalName = manifest.originalName

  status.innerText = "并行下载中..."

  let done = 0

  // ================= 并行下载 =================
  const results = await asyncPool(4, list, async (item)=>{

    const r = await fetch(`uploads/${id}/${item.name}`)
    const enc = await r.text()

    const decrypted = CryptoJS.AES.decrypt(enc, key)

    const base64 = decrypted.toString(CryptoJS.enc.Base64)

    const binary = atob(base64)

    const arr = new Uint8Array(binary.length)

    for(let i=0;i<binary.length;i++){
      arr[i] = binary.charCodeAt(i)
    }

    done++
    bar.value = (done / list.length) * 100
    status.innerText = `下载 ${done}/${list.length}`

    return {
      index: list.indexOf(item),
      data: arr
    }
  })

  // ================= 排序重组 =================
  results.sort((a,b)=>a.index-b.index)

  let total = 0
  results.forEach(r=> total += r.data.length)

  const merged = new Uint8Array(total)

  let offset = 0

  for(const r of results){
    merged.set(r.data, offset)
    offset += r.data.length
  }

  // ================= 下载文件 =================
  const blob = new Blob([merged])

  const a = document.createElement("a")
  a.href = URL.createObjectURL(blob)
  a.download = originalName

  a.click()

  status.innerText = "下载完成"
  bar.value = 100
}
</script>

</body>
</html>

file.php

<?php

$uploadDir = 'uploads/';
$action = $_GET['action'] ?? '';

header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');

function out($data){
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

/**
 * ======================
 * upload
 * ======================
 */
if($action === 'upload'){

    $id = $_POST['id'] ?? null;
    if(!$id){
        $id = uniqid("f_");
    }

    $dir = $uploadDir . $id;
    if(!is_dir($dir)){
        mkdir($dir, 0755, true);
    }

    // chunk
    if(isset($_POST['chunk']) && isset($_POST['name'])){

        file_put_contents($dir . "/" . $_POST['name'], $_POST['chunk']);

        out([
            "status"=>"ok",
            "type"=>"chunk",
            "id"=>$id
        ]);
    }

    // manifest
    if(isset($_POST['manifest'])){

        file_put_contents($dir . "/manifest.json", $_POST['manifest']);

        out([
            "status"=>"ok",
            "type"=>"manifest",
            "id"=>$id
        ]);
    }

    out(["status"=>"error","msg"=>"invalid"]);
}


/**
 * ======================
 * download
 * ======================
 */
if($action === 'send'){

    $id = $_GET['id'] ?? '';
    $dir = $uploadDir . $id;
    $file = $dir . "/manifest.json";

    if(!file_exists($file)){
        out(["status"=>"error","msg"=>"not found"]);
    }

    $manifest = json_decode(file_get_contents($file), true);

    out([
        "status"=>"ok",
        "manifest"=>$manifest,
        "id"=>$id
    ]);
}