You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaScript & Node 分片上传图片保存至服务器后无法预览

问题排查结论

首先直接回答核心疑问:将buffer写入文件前不需要裁剪剔除任何内容,保存的图片无法预览是前后端分片上传逻辑存在多处错误导致的,和原始文件buffer的内容无关。
具体错误点如下:

  • 后端写入逻辑错误:每次收到分片都调用fs.writeFileSync,该方法默认是覆盖写入模式,最终服务器上的文件只会保留最后一个收到的分片内容,不可能是完整图片。
  • 写入编码配置错误:调用写入方法时传入了'base64'编码参数,但前端发送的是原始二进制ArrayBuffer数据,并非base64编码格式,编码不匹配会直接导致写入的二进制内容被转码损坏。
  • 中间件冲突:全局挂载的express.json()中间件会尝试把所有请求体解析为JSON格式,会破坏发送的原始二进制流数据。
  • 前端分片逻辑有缺陷:分片数量计算没有做向上取整,循环条件多写了+1,会额外生成空分片发送;且请求头没有携带分片序号,就算改成追加写入,一旦网络请求乱序到达,拼接出来的文件内容顺序也是错的。
  • 端口配置不匹配:前端请求写的是8000端口,后端默认监听5000端口,联调时需要统一配置。
修复方案

前端代码调整

  1. 修正分片计算逻辑,用Math.ceil取整计算总分片数,去掉循环条件多余的+1,跳过空分片
  2. 请求头新增分片序号、总分片数字段,供后端校验拼接顺序
    调整后的核心代码:
const getImageInput = document.getElementById('imageInput');
getImageInput.addEventListener('change',(event)=>{
  uploadImage(event.target.files)
})

function uploadImage(files){
  Array.from(files).forEach((file)=>{
    imageUploadApi(file)
  })
}

async function imageUploadApi(file){
  const reader = new FileReader()
  reader.readAsArrayBuffer(file)
  reader.addEventListener('load',async(event)=>{
    const CHUNK_SIZE = 1000
    const totalLength = event.target.result.byteLength
    // 向上取整计算总分片数
    const chunkCount = Math.ceil(totalLength / CHUNK_SIZE)
    const fileName = file.name
    for(let i = 0; i < chunkCount; i++){
      const start = i * CHUNK_SIZE
      const end = Math.min(start + CHUNK_SIZE, totalLength)
      const chunk = event.target.result.slice(start, end)
      // 空分片直接跳过
      if(chunk.byteLength === 0) continue
      const response = await fetch('http://localhost:8000/image-upload',{
        method:'POST',
        body: chunk,
        headers:{
          'Content-Type':'application/octet-stream',
          'Content-Length':chunk.length,
          'file-name': fileName,
          'chunk-index': i, // 新增当前分片序号
          'total-chunks': chunkCount // 新增总分片数
        }
      })
      console.log(await response.json())
    }
  })
}

后端代码调整

  1. 调整中间件匹配规则,避免express.json()处理二进制上传请求,用express.raw()专门解析octet-stream类型的请求体,配置合适的请求体大小上限
  2. 写入文件改用对应模式:第一个分片写入时清空创建新文件,后续分片用追加模式写入,去掉错误的base64编码参数
  3. 补全缺失的path模块引用,统一监听端口和前端请求端口一致
    调整后的核心代码:
const express = require('express');
const app = express();
const cors = require('cors');
const fs = require('fs');
const path = require('path');

app.use(cors());
// 仅对json类型请求使用json解析器
app.use(express.json({
  type: ['application/json']
}));
// 单独给二进制上传接口配置raw解析器
app.post('/image-upload', express.raw({type: 'application/octet-stream', limit: '100mb'}), (req,res)=>{
  const fileName = req.headers['file-name']
  const chunkIndex = Number(req.headers['chunk-index'])
  const savePath = path.join(__dirname, './upload_pictures/', fileName)
  // 第一个分片先清空创建文件,后续分片追加写入
  if(chunkIndex === 0){
    fs.writeFileSync(savePath, req.body)
  }else{
    fs.appendFileSync(savePath, req.body)
  }
  res.status(200).json({
    message:'Chunk Uploaded Successfully'
  })
})

if(process.env.NODE_ENV === 'production'){
  app.use(express.static('client/build'));
  app.get('*', (req, res) =>{
    res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
  });
}

const port = process.env.PORT || 8000;
app.listen(port, () => console.log(`Server started on port ${port}`));

注意:运行代码前先确认upload_pictures文件夹已经创建,否则写入会报路径不存在的错误。


内容的提问来源于stack exchange,提问作者night_programmer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 10:33:22