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

如何编程将外部URL的PDF文件上传至自定义内容类型文章

BODS自定义内容类型存储外部URL指向PDF的编程实现

你之前用的图片上传方案不能直接复用,核心原因是BODS的图片处理链路默认绑定了image/*类型白名单、缩略图生成、EXIF信息读取逻辑,PDF属于application/pdf类型,直接调用图片接口会触发类型校验拦截,而且文件字段如果用了图片专用存储驱动,也无法识别非图片格式的文件实体。

前置配置调整

先改自定义内容类型的文件字段配置,不然代码写了也会被拦截:

  • 找到你添加的文件字段,在允许上传的MIME类型列表里新增application/pdf
  • 关闭字段的「强制本地文件上传校验」开关,开启「允许外部资源引用」选项(对应版本有该配置项时操作)
  • 将字段的存储驱动从「图片专用存储」切换为「通用文件存储」,跳过图片专属处理流程

具体实现逻辑

1. 拉取并校验外部PDF资源

不要直接把外部URL存进字段,先做本地拉取校验,规避死链、伪造文件问题:

$externalPdfUrl = "目标PDF的外部地址";
// 拉取资源
$ch = curl_init($externalPdfUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$fileContent = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);

// 合法性校验
if ($httpCode != 200) {
    throw new \Exception("外部PDF拉取失败,HTTP状态码:{$httpCode}");
}
if (!str_contains($contentType, 'application/pdf')) {
    throw new \Exception("目标资源不是PDF格式,实际类型:{$contentType}");
}
// 校验文件头,防后缀伪造
if (!str_starts_with($fileContent, '%PDF')) {
    throw new \Exception("文件内容不符合PDF格式规范");
}

2. 写入通用文件存储

调用通用文件存储接口,不要用图片专用的存储方法:

// 生成唯一文件名,避免重名覆盖
$fileName = md5($externalPdfUrl . time()) . '.pdf';
// 写入通用文件存储
$fileEntity = \BODS\File\FileService::saveGeneralFile(
    content: $fileContent,
    fileName: $fileName,
    mimeType: 'application/pdf',
    meta: [
        'source_url' => $externalPdfUrl,
        'is_external_source' => 1
    ]
);

3. 关联到自定义内容类型字段

拿到文件实体ID后,绑定到对应内容的文件字段上:

// 初始化自定义内容类型实体
$contentEntity = \BODS\Content\ContentService::createEntity('自定义内容类型的机器名');
// 给文件字段赋值,注意传入通用文件实体ID,不要传图片实体ID
$contentEntity->setFieldValue('文件字段的机器名', [$fileEntity->id]);
$contentEntity->save();

常见踩坑

  • 不要直接把外部URL字符串赋值给文件字段:BODS文件字段默认关联本地文件实体表,直接存URL会导致前端渲染失败、权限校验失效
  • 50M以上的大PDF不要直接拉取到内存,改用临时文件流式写入,避免内存溢出
  • 文件存储完成后,要给文件实体配置和内容类型一致的访问权限,否则前台会提示无权限访问
  • SaaS版无服务端代码权限的场景,可以走webhook+开放接口实现:触发流程时先调用通用文件上传接口传PDF拿到文件ID,再调用内容更新接口绑定ID即可,逻辑和上述代码一致

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:18:17