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

Swift中base64转Data返回nil 如何正确保存PDF到Firebase存储

问题根因

Swift端Data(base64Encoded:)返回nil不存在什么特殊语法问题,本质是拿到的base64字符串不符合API的解码要求,或是两端编解码逻辑没对齐。下面是两种可直接落地的修复方案:


方案1:直接在云函数端完成PDF上传(最稳妥,推荐)

把base64字符串从云函数传到前端再解码上传完全是多此一举,既容易因为传输过程的字符转义导致base64损坏,还浪费客户端流量,云函数和Firebase Storage走内网传输速度快得多,也不会有编解码问题。

修复后的Node.js云函数代码

const { initializeApp } = require('firebase-admin/app');
const { getStorage } = require('firebase-admin/storage');
const { getFirestore } = require('firebase-admin/firestore');
initializeApp();
const bucket = getStorage().bucket();
const db = getFirestore();

exports.getAssetReportPdf = functions.https.onCall(async (data, context) => {
  // 必须加鉴权,否则接口裸奔任何人都能拉取用户资产报告
  if (!context.auth) {
    throw new functions.https.HttpsError('unauthenticated', '登录后才可操作');
  }
  const { assetReportToken, phone } = data;

  const configuration = new Configuration({
    basePath: PlaidEnvironments[functions.config().app.env],
    baseOptions: {
      headers: {
        "PLAID-CLIENT-ID": functions.config().plaid.client_id,
        "PLAID-SECRET": functions.config().plaid.secret,
      },
      // 强制返回二进制数组,避免SDK自动转utf8字符串损坏PDF内容
      responseType: 'arraybuffer'
    },
  });

  const plaidClient = new PlaidApi(configuration);

  try {
    const response = await plaidClient.assetReportPdfGet({ asset_report_token: assetReportToken });
    const pdfBuffer = Buffer.from(response.data);
    // 上传到Storage,路径和你之前前端逻辑保持一致
    const file = bucket.file(`${phone}/${phone}`);
    await file.save(pdfBuffer, {
      contentType: 'application/pdf'
    });
    // 生成下载URL
    const [downloadUrl] = await file.getSignedUrl({
      action: 'read',
      expires: '03-01-2500'
    });
    // 直接把URL写入Firestore,省掉前端写库逻辑
    await db.collection('users').doc(phone).set({
      asset_report: downloadUrl
    }, { merge: true });

    return { success: true, url: downloadUrl };
  } catch (err) {
    console.error('拉取PDF失败', err);
    throw new functions.https.HttpsError("internal", "PDF下载失败:" + err.message);
  }
});

对应的Swift端代码可以大幅简化,不需要处理任何base64转码、上传Storage的逻辑,拿到返回的URL直接用即可。


方案2:保留前端转码上传逻辑,修复base64解码问题

如果一定要走前端转base64的流程,注意Data(base64Encoded:)对字符串格式要求极严:字符串长度必须是4的倍数、不能带换行/空格/转义字符等多余内容,必须先做清洗再解码。

Swift端解码正确写法

guard let res = result?.data as? String else {
    debugPrint("返回数据格式错误,不是字符串类型")
    return
}
// 1. 清洗所有换行、空格、转义字符
var cleanedBase64 = res.replacingOccurrences(
    of: "[\\s\\n\\r\\t\\\"]", 
    with: "", 
    options: .regularExpression
)
// 2. 补全末尾缺失的=填充符,保证长度是4的倍数
let padCount = (4 - cleanedBase64.count % 4) % 4
cleanedBase64 += String(repeating: "=", count: padCount)
// 3. 带忽略未知字符选项解码
guard let dataPDF = Data(
    base64Encoded: cleanedBase64, 
    options: .ignoreUnknownCharacters
) else {
    debugPrint("base64解码失败,字符串前缀:\(cleanedBase64.prefix(100))")
    return
}
// 后续上传Storage的逻辑保持不变即可

同时Node端要确认response.data是原始Buffer类型,不要先转成utf8字符串再转base64,否则会直接损坏二进制内容。


额外避坑提醒

  • 你代码里大量用强制解包!,只要documentID、dataPDF、url任意一个值为nil,APP就会直接崩溃,全部改成guard let做安全校验
  • 不建议用手机号作为文件名存储敏感资产报告,最好拼接随机非序列字符串,避免被恶意遍历下载
  • 云函数端不要信任前端传的用户标识,优先从context.auth.uid拿用户ID,避免用户篡改请求参数上传/下载其他人的报告

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 14:33:27