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

如何将SPA传来的PDF经Azure Function App存入Blob Storage

问题描述

我在将PDF保存到Azure Blob Storage时遇到了困难。该PDF由SPA上传至Azure Function App,目前已实现以下流程:

SPA端POST请求代码

const uploadFunction = async () => {
  if (!selectedDocuments || !targetLanguage) return;

  const formData = new FormData();
  formData.append("targetLanguage", targetLanguage);

  [...selectedDocuments].map((document, index) => {
    formData.append(`document${index + 1}`, document);
  });

  const translate = await fetch("http://localhost/api/TranslateFunction", {
    method: "POST",
    body: formData,
    headers: {
      "Content-Type": "undefined",
    },
  });

  console.log(translate);
};

Azure Function App端代码

[Function("TranslateFunction")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req, FunctionContext executionContent)
{   
    string connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
    string sourceContainer = Environment.GetEnvironmentVariable("SourceContainer");
    string targetContainer = Environment.GetEnvironmentVariable("TargetContainer");

    var sourceContainerClient = new BlobContainerClient(connection, sourceContainer);
    var targetContainerClient = new BlobContainerClient(connection, targetContainer);

    string body = await new StreamReader(req.Body).ReadToEndAsync();

    var response = req.CreateResponse(HttpStatusCode.OK);
    return response;
}

我认为PDF内容存在HttpRequestData的请求体中,但不知道如何将该字符串内容作为PDF上传到Blob Storage,是否需要使用NuGet包将字符串转换为PDF后再上传?现有教程多是从文件系统读取PDF后存储,但我的场景是PDF通过HTTP请求传递。

编辑补充:

读取请求体后发现已接收到PDF内容(如下所示):

------WebKitFormBoundaryxQpq3BGLOOwB4z2I
Content-Disposition: form-data; name="document1"; filename="one.pdf"
Content-Type: application/pdf

%PDF-1.4
%����

1 0 obj
<< /Type /Catalog
/Pages 2 0 R
/PageLayout /TwoColumnRight
/Outlines 7 0 R>>
endobj

9 0 obj
<< /Title (Cutaneous receptors)
/Parent 8 0 R
/Dest [3 0 R /XYZ 0 420 0]
/Next 10 0 R>>
endobj

10 0 obj
<< /Title (Nociceptors)
/Parent 8 0 R
/Dest [4 0 R /XYZ 0 124 0]
/Prev 9 0 R
/Next 11 0 R>>
endobj

11 0 obj
<< /Title (Muscle Spindles)
/Parent 8 0 R
/Dest [5 0 R /XYZ 0 201 0]
/Prev 10 0 R
/Next 12 0 R>>
endobj

12 0 obj
<< /Title (Joint receptors)
/Parent 8 0 R
/Dest [6 0 R /XYZ 0 174 0]
/Prev 11 0 R>>
endobj

8 0 obj
<< /Title (Anatomy of the Somatosensory System)
/Parent 7 0 R
/Dest [3 0 R /XYZ 0 624 0]
/First 9 0 R
/Last 12 0 R
/Count 4>>
endobj

7 0 obj
<< /Type /Outlines
/First 8 0 R
/Last 8 0 R>>
endobj

2 0 obj
<< /Type /Pages
/Kids [3 0 R 4 0 R 5 0 R 6 0 R]
/Count 4>>
endobj

20 0 obj
<< /Length 21 0 R
/Filter /FlateDecode>>
stream
x���َe�r�^_����f�
解决方案

无需额外NuGet包转换字符串,当前核心问题是直接读取了带边界标识的多部分表单请求体为字符串,破坏了二进制文件数据。正确做法是解析FormData提取PDF文件流,直接上传到Blob Storage。

步骤1:修正SPA请求的Content-Type设置

删除SPA请求中手动设置的"Content-Type": "undefined",fetch会自动根据FormData生成正确的multipart/form-data请求头(包含边界标识),手动设置会导致后端解析失败。

修改后的SPA代码:

const uploadFunction = async () => {
  if (!selectedDocuments || !targetLanguage) return;

  const formData = new FormData();
  formData.append("targetLanguage", targetLanguage);

  [...selectedDocuments].map((document, index) => {
    formData.append(`document${index + 1}`, document);
  });

  const translate = await fetch("http://localhost/api/TranslateFunction", {
    method: "POST",
    body: formData
    // 移除手动设置的Content-Type
  });

  console.log(translate);
};

步骤2:在Azure Function中解析多部分表单数据

使用MultipartReader解析多部分表单,直接提取文件流上传。需确保引用Microsoft.AspNetCore.WebUtilities包(默认已包含,若缺失可通过NuGet安装)。

修改后的Function代码:

using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Net.Http.Headers;

[Function("TranslateFunction")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req, FunctionContext executionContext)
{   
    string connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
    string sourceContainer = Environment.GetEnvironmentVariable("SourceContainer");
    var sourceContainerClient = new BlobContainerClient(connection, sourceContainer);
    // 确保目标容器存在
    await sourceContainerClient.CreateIfNotExistsAsync();

    // 解析多部分表单边界
    var contentType = MediaTypeHeaderValue.Parse(req.Headers.GetValues("Content-Type").First());
    var boundary = MultipartHeaders.GetBoundary(contentType);
    var reader = new MultipartReader(boundary, req.Body);
    MultipartSection section;

    while ((section = await reader.ReadNextSectionAsync()) != null)
    {
        var contentDisposition = ContentDispositionHeaderValue.Parse(section.ContentDisposition);
        string fileName = contentDisposition.FileName?.Value.Trim('"');
        
        // 判断当前部分是否为文件字段
        if (!string.IsNullOrEmpty(fileName))
        {
            // 直接将文件流上传到Blob Storage
            var blobClient = sourceContainerClient.GetBlobClient(fileName);
            await blobClient.UploadAsync(section.Body, overwrite: true);
        }
        else
        {
            // 处理非文件参数(如targetLanguage)
            string paramName = contentDisposition.Name.Value.Trim('"');
            string paramValue = await new StreamReader(section.Body).ReadToEndAsync();
            // 可在此保存参数值供后续逻辑使用
        }
    }

    var response = req.CreateResponse(HttpStatusCode.OK);
    await response.WriteStringAsync("文件上传完成");
    return response;
}

关键说明

  • 禁止直接读取请求体为字符串:多部分表单包含二进制文件内容,转字符串会破坏数据,必须用流直接处理。
  • 依赖自动生成的Content-Type:SPA端无需手动设置,fetch会自动添加正确的请求头,保证后端能解析边界标识。
  • 无需转换PDF格式:上传的文件本身就是PDF二进制流,直接上传即可,不需要额外转换工具。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 08:45:05