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

如何在Apex/Salesforce中实现带删除按钮的分片多部分文件上传

基于JavaScript实现Salesforce分片上传的完整实现方案

前置准备

首先完成Salesforce端配置,避免权限或跨域问题:

  • 配置Salesforce CORS规则,允许前端域名访问
  • OAuth2授权获取有效访问Token,确保账号拥有Create ContentVersion、Delete ContentVersion权限
  • 使用不低于v48.0的Salesforce REST API版本

核心实现代码

1. 前端DOM结构

<!-- 文件上传控件 -->
<input type="file" id="fileUpload" multiple>
<!-- 进度条 -->
<div style="width: 100%; height: 8px; background: #eee; margin: 10px 0;">
  <div id="uploadProgress" style="height: 100%; width: 0%; background: #2e84fa;"></div>
</div>
<!-- 已上传文件列表 -->
<ul id="uploadedFileList"></ul>

2. JavaScript逻辑实现

首先定义可配置参数,满足自定义分片大小需求:

// 可自定义分片大小,示例为5MB,可根据需求调整
const CHUNK_SIZE = 5 * 1024 * 1024;
const SF_API_VERSION = 'v58.0';
const SF_INSTANCE_URL = '你的Salesforce实例域名';
let accessToken = '你的OAuth授权Token';
let uploadedFiles = []; // 存储已上传文件信息

分片上传核心逻辑

const fileUploadDom = document.getElementById('fileUpload');
const progressDom = document.getElementById('uploadProgress');
const fileListDom = document.getElementById('uploadedFileList');

fileUploadDom.addEventListener('change', async (e) => {
  const file = e.target.files[0];
  if (!file) return;
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
  let uploadedSize = 0;
  progressDom.style.width = '0%';

  try {
    // 第一步:创建Salesforce分片上传会话
    const sessionRes = await fetch(`${SF_INSTANCE_URL}/services/data/${SF_API_VERSION}/connect/files/users/me`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        title: file.name,
        fileType: file.name.split('.').pop(),
        totalParts: totalChunks
      })
    });
    const sessionData = await sessionRes.json();
    const uploadSessionId = sessionData.id;

    // 第二步:逐个上传分片
    for (let i = 0; i < totalChunks; i++) {
      const start = i * CHUNK_SIZE;
      const end = Math.min(start + CHUNK_SIZE, file.size);
      const chunk = file.slice(start, end);

      await fetch(`${SF_INSTANCE_URL}/services/data/${SF_API_VERSION}/connect/files/users/me/${uploadSessionId}/parts/${i+1}`, {
        method: 'PUT',
        headers: {
          'Authorization': `Bearer ${accessToken}`,
          'Content-Type': 'application/octet-stream'
        },
        body: chunk
      });

      uploadedSize += chunk.size;
      // 更新进度条
      progressDom.style.width = `${Math.round((uploadedSize / file.size) * 100)}%`;
    }

    // 第三步:合并分片完成上传
    const completeRes = await fetch(`${SF_INSTANCE_URL}/services/data/${SF_API_VERSION}/connect/files/users/me/${uploadSessionId}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${accessToken}`,
        'Content-Type': 'application/json'
      }
    });
    const completedFile = await completeRes.json();
    
    // 保存已上传文件信息,渲染到列表
    const fileInfo = {
      id: completedFile.contentDocumentId,
      name: file.name,
      size: file.size
    };
    uploadedFiles.push(fileInfo);
    renderFileList();
  } catch (err) {
    console.error('上传失败:', err);
    progressDom.style.width = '0%';
  }
});

已上传文件列表渲染&删除逻辑

// 渲染已上传文件列表
function renderFileList() {
  fileListDom.innerHTML = '';
  uploadedFiles.forEach((file, index) => {
    const li = document.createElement('li');
    li.innerHTML = `
      <span>${file.name} (${Math.round(file.size / 1024 / 1024)}MB)</span>
      <button data-index="${index}" class="delete-btn">删除</button>
    `;
    fileListDom.appendChild(li);
  });

  // 绑定删除事件
  document.querySelectorAll('.delete-btn').forEach(btn => {
    btn.addEventListener('click', async (e) => {
      const index = e.target.dataset.index;
      const fileId = uploadedFiles[index].id;
      
      // 调用Salesforce接口删除文件
      await fetch(`${SF_INSTANCE_URL}/services/data/${SF_API_VERSION}/sobjects/ContentDocument/${fileId}`, {
        method: 'DELETE',
        headers: {
          'Authorization': `Bearer ${accessToken}`
        }
      });

      // 更新本地列表
      uploadedFiles.splice(index, 1);
      renderFileList();
    });
  });
}

功能对应说明

  • 支持文件分片上传:通过file.slice切割文件,调用Salesforce分块上传接口逐片上传,支持大文件上传避免超时
  • 已上传文件展示:上传完成后将文件信息存入本地数组,调用renderFileList动态渲染到DOM
  • 删除已上传文件:每个文件项绑定删除事件,调用Salesforce删除接口后同步更新本地列表
  • 自定义分片大小:修改CHUNK_SIZE变量即可调整分片大小,建议不低于1MB、不超过30MB
  • 上传进度指示:每成功上传一个分片后计算已上传占比,实时更新进度条宽度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 18:27:04