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

如何将Base64或Blob格式的PDF文件附加到Azure DevOps工作项?

问题描述

尝试将云端下载的Blob格式PDF附加到Azure DevOps工作项时失败,错误提示无法从本地获取该文件(实际文件为Blob而非本地文件)。目前附加文本文件可正常运行,当前使用的上传函数如下:

错误截图

const convertBlobToBase64 = (blob: Blob): Promise<string> => {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    reader.readAsDataURL(blob)
    reader.onloadend = () => {
      const base64data = reader.result?.toString().split(',')[1]
      resolve(base64data || '')
    }
    reader.onerror = reject
  })
}

export const addPdfAttachmentToWorkItem = async (
  workItemId: number,
  pat: string,
  organizationUrl: string,
  projectId: string,
  pdfBlob: Blob,
  fileName: string
) => {
  try {
    const encodedPat = btoa(`:${pat}`)
    const url = `${organizationUrl}/${projectId}/_apis/wit/attachments?api-version=6.0&fileName=${fileName}&uploadType=simple`
    const pdfBase64 = await convertBlobToBase64(pdfBlob)
    const attachRes = await axios({
      method: 'POST',
      url,
      data: pdfBase64,
      headers: {
        Authorization: `Basic ${encodedPat}`,
        'Content-Type': 'application/octet-stream',
      },
    })

    const witUrl = `${organizationUrl}/${projectId}/_apis/wit/workitems/${workItemId}?api-version=6.0`
    const workItemData = [
      {
        op: 'add',
        path: `/relations/-`,
        value: {
          rel: 'AttachedFile',
          url: attachRes.data.url,
          attributes: {
            comment: `Attached work package report for WIT ${workItemId}`,
          },
        },
      },
    ]
    const response = await axios.patch(witUrl, workItemData, {
      headers: {
        Authorization: `Basic ${encodedPat}`,
        'Content-Type': 'application/json-patch+json',
      },
      params: {
        $expand: 'all',
      },
    })
    return response.data
  } catch (error) {
    console.error('Error updating work item:', error)
    throw error
  }
}
问题分析

当前代码将Blob转换为Base64字符串后上传,文本文件因编码简单可兼容,但PDF作为二进制文件,Base64转换过程中可能出现编码损耗,且Axios对Base64字符串作为application/octet-stream类型数据的处理逻辑,容易导致二进制数据传输不完整,触发服务端错误。

解决方案

直接上传Blob原始数据,跳过Base64转换步骤,确保二进制数据完整传输:

export const addPdfAttachmentToWorkItem = async (
  workItemId: number,
  pat: string,
  organizationUrl: string,
  projectId: string,
  pdfBlob: Blob,
  fileName: string
) => {
  try {
    const encodedPat = btoa(`:${pat}`)
    const url = `${organizationUrl}/${projectId}/_apis/wit/attachments?api-version=6.0&fileName=${fileName}&uploadType=simple`
    
    // 直接发送Blob原始数据,无需Base64转换
    const attachRes = await axios({
      method: 'POST',
      url,
      data: pdfBlob,
      headers: {
        Authorization: `Basic ${encodedPat}`,
        'Content-Type': 'application/octet-stream',
        // 明确指定数据长度,保证服务端正确接收
        'Content-Length': pdfBlob.size.toString()
      },
      // 阻止Axios默认序列化,保留Blob原始格式
      transformRequest: [(data) => data]
    })

    const witUrl = `${organizationUrl}/${projectId}/_apis/wit/workitems/${workItemId}?api-version=6.0`
    const workItemData = [
      {
        op: 'add',
        path: `/relations/-`,
        value: {
          rel: 'AttachedFile',
          url: attachRes.data.url,
          attributes: {
            comment: `Attached work package report for WIT ${workItemId}`,
          },
        },
      },
    ]
    const response = await axios.patch(witUrl, workItemData, {
      headers: {
        Authorization: `Basic ${encodedPat}`,
        'Content-Type': 'application/json-patch+json',
      },
      params: {
        $expand: 'all',
      },
    })
    return response.data
  } catch (error) {
    console.error('Error updating work item:', error)
    throw error
  }
}
额外检查点
  1. 确认Blob的MIME类型为application/pdf,若下载的Blob类型不正确,可重新构造Blob:
    const correctedPdfBlob = new Blob([pdfBlob], { type: 'application/pdf' })
    
  2. 验证PAT权限,确保拥有Work Item Write和Attachment Write权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 22:23:26