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
相关产品推荐
相关产品推荐

