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

Rails 7中使用Faraday向第三方API上传PDF文件的问题

问题:Rails 7中使用Faraday发送PDF文件到第三方API

需要向第三方API发送应用生成的PDF文件,API提供的curl示例如下:

curl -X POST --header 'Content-Type: multipart/form-data' --header 'Accept: application/json' {"type":"formData"} 'https://some_path.com'

现有Faraday客户端和资源类实现如下,但无法正确发送文件,触发API错误:

# app/clients/test_api/client.rb
module TestApi
  class Client
    include ::Errors

    API_ENDPOINT = 'https://some_path.com'
    ACCESS_TOKEN = Rails.application.credentials.api_token

    def initialize
      @access_token = ACCESS_TOKEN
    end

    def post(path, options = {})
      handle_response(client.public_send(:post, path.to_s, options.to_json))
    end

    attr_reader :access_token

    private

    def client
      @client =
        Faraday.new(API_ENDPOINT) do |client|
          client.request :url_encoded
          client.response :json, content_type: /\bjson$/
          client.adapter Faraday.default_adapter
          client.headers['Accept'] = 'application/json'
          client.headers['Content-Type'] = 'application/json'
          client.headers['apiToken'] = access_token.to_s if access_token.present?
        end
    end

    def handle_response(response)
      return response_body(response) if response.success?

      raise error_class(response.status)
    end

    def response_body(response)
      response&.body
    end
  end
end
# app/clients/test_api/resources.rb
module TestApi
  class Resources
    def upload_document(file)
      client.post('/sspfile/uploadtemporary', body: file)
    end

    private

    def client
      @client ||= Client.new
    end
  end
end

问题分析

原实现的核心问题在于:

  • 强制全局设置Content-Type: application/json,但API要求multipart/form-data
  • 使用url_encoded请求适配器,不支持文件上传的multipart格式
  • 调用post时将参数转为JSON,不符合multipart请求的数据格式要求

修正后的实现

1. 调整Client类,支持multipart请求

# app/clients/test_api/client.rb
module TestApi
  class Client
    include ::Errors

    API_ENDPOINT = 'https://some_path.com'
    ACCESS_TOKEN = Rails.application.credentials.api_token

    def initialize
      @access_token = ACCESS_TOKEN
    end

    # 拆分普通POST和multipart POST方法,避免格式冲突
    def post(path, body = {}, headers = {})
      handle_response(client.post(path.to_s, body, headers))
    end

    def post_multipart(path, form_data, headers = {})
      handle_response(client.post(path.to_s) do |req|
        req.headers.merge!(headers)
        req.body = form_data
      end)
    end

    attr_reader :access_token

    private

    def client
      @client =
        Faraday.new(API_ENDPOINT) do |client|
          # 添加multipart请求适配器,支持文件上传
          client.request :multipart
          client.request :url_encoded # 保留用于普通表单请求,可选
          client.response :json, content_type: /\bjson$/
          client.adapter Faraday.default_adapter
          client.headers['Accept'] = 'application/json'
          # 不要全局设置Content-Type,让multipart适配器自动处理
          client.headers['apiToken'] = access_token.to_s if access_token.present?
        end
    end

    def handle_response(response)
      return response_body(response) if response.success?

      raise error_class(response.status)
    end

    def response_body(response)
      response&.body
    end
  end
end

2. 调整Resources类,正确传递文件参数

# app/clients/test_api/resources.rb
module TestApi
  class Resources
    # file参数应为File对象、Tempfile或其他可读取的文件对象
    def upload_document(file)
      # 构建multipart表单数据,字段名需和API要求一致(示例用"file",需按实际调整)
      form_data = Faraday::Multipart::FormDataPart.new(
        file: Faraday::Multipart::FilePart.new(file, 'application/pdf', 'document.pdf')
      )
      client.post_multipart('/sspfile/uploadtemporary', form_data)
    end

    private

    def client
      @client ||= Client.new
    end
  end
end

关键注意事项

  • 确保传入的file是可读取的文件对象(比如Rails生成PDF后得到的Tempfile,或磁盘上的File实例)
  • 表单字段名(示例中的file)必须和第三方API要求的字段名完全匹配,否则会触发API错误
  • FilePart的第二个参数是文件MIME类型,PDF对应application/pdf;第三个参数是显示用的文件名,可根据实际需求修改

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 06:57:09