使用QuickBooks API上传JPG图片遇500错误及gem调用问题求助
Hi there! I know you've been troubleshooting this for two days, so let's get straight to fixing your QBO API upload issues.
1. Fixing the 500 "Unsupported Content Type" Error (Direct Signet Request)
The root cause here is that QBO's /upload endpoint requires a multipart/form-data request, but your current code is sending application/json with an incorrectly formatted body. The error message clearly states it can't consume the content type you're sending.
What's Wrong with Your Current Request:
- You set
Content-Type: application/jsonbut added a boundary (which is exclusively for multipart requests) - You're only sending the JSON metadata, not including the actual file binary data in the request
- QBO expects two distinct parts in the multipart request:
- A JSON part with the
Attachablemetadata (likeFileName,AttachableRef) - A binary part with the raw file content
- A JSON part with the
Corrected Code:
Here's how to adjust your Signet code to send a valid multipart request:
@client = Signet::OAuth1::Client.new( :temporary_credential_uri => "https://oauth.intuit.com/oauth/v1/get_request_token", :authorization_uri => "https://appcenter.intuit.com/Connect/Begin", :token_credential_uri => "https://oauth.intuit.com/oauth/v1/get_access_token", :client_credential_key => App.find_by_name('quickbooks').clientid, :client_credential_secret => App.find_by_name('quickbooks').clientsecret ) @client.token_credential_key = quickbooks.accesstoken @client.token_credential_secret = quickbooks.refreshtoken realmid = quickbooks.uid url = Rails.env.development? ? "sandbox-quickbooks.api.intuit.com" : "quickbooks.api.intuit.com" # Prepare the metadata JSON metadata = { "AttachableRef": [ { "EntityRef": { "type": "Invoice", "value": "95" } } ], "FileName": params[:file].original_filename, "ContentType": "image/jpg" }.to_json # Generate a unique boundary for multipart formatting boundary = "----WebKitFormBoundary#{SecureRandom.hex(10)}" # Build the multipart request body request_body = [ "--#{boundary}", 'Content-Disposition: form-data; name="metadata"', 'Content-Type: application/json', '', metadata, "--#{boundary}", "Content-Disposition: form-data; name=\"file\"; filename=\"#{params[:file].original_filename}\"", "Content-Type: image/jpg", '', params[:file].read, "--#{boundary}--", '' ].join("\r\n") # Send the request with correct headers original_request = [ 'Post', "https://#{url}/v3/company/#{realmid}/upload", [ ['Content-Type', "multipart/form-data; boundary=#{boundary}"] ], request_body ] response = @client.fetch_protected_resource(:request => original_request)
Key changes:
- Set
Content-Typetomultipart/form-datawith a valid, unique boundary - Included both the metadata JSON and file binary in the request body
- Followed proper multipart formatting rules with boundary separators
2. Fixing the NoMethodError: undefined method 'post_with_multipart' (quickbook-ruby Gem)
This error occurs because the quickbook-ruby gem expects an OAuth::AccessToken object for the :access_token parameter, but you're passing a plain string. The gem tries to call post_with_multipart on the token, which doesn't exist on a String.
Corrected Code:
First, create a valid OAuth::AccessToken instance using your consumer and token credentials, then pass that to the Upload service:
# Initialize your OAuth consumer with your actual app credentials OAUTH_CONSUMER_KEY = App.find_by_name('quickbooks').clientid OAUTH_CONSUMER_SECRET = App.find_by_name('quickbooks').clientsecret ::QB_OAUTH_CONSUMER = OAuth::Consumer.new( OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, { :site => "https://oauth.intuit.com", :request_token_path => "/oauth/v1/get_request_token", :authorize_url => "https://appcenter.intuit.com/Connect/Begin", :access_token_path => "/oauth/v1/get_access_token" } ) # Create a valid OAuth::AccessToken instance using your stored tokens access_token = OAuth::AccessToken.new( QB_OAUTH_CONSUMER, quickbooks.accesstoken, # Your stored access token string quickbooks.refreshtoken # Your stored refresh token string ) # Prepare the Attachable metadata meta = Quickbooks::Model::Attachable.new meta.file_name = "monkey.jpg" meta.note = "A note" meta.content_type = "image/jpg" entity = Quickbooks::Model::BaseReference.new(123, type: 'Transfer') meta.attachable_ref = Quickbooks::Model::AttachableRef.new(entity) # Initialize the Upload service with the AccessToken object (not a string) upload_service = Quickbooks::Service::Upload.new( :realm_id => quickbooks.uid, # Your actual realm ID :access_token => access_token ) # Upload the file (ensure doc_path is a valid path to your local file) doc_path = '/path/to/your/monkey.jpg' result = upload_service.upload(doc_path, meta)
Key fixes:
- Created a proper
OAuth::AccessTokeninstead of passing a raw string - Passed the
metaobject toupload()(the gem uses this to inject the correct metadata) - Used your actual stored realm ID, access token, and refresh token from your
quickbooksrecord
Final Tips:
- For the gem approach, run
bundle update quickbooks-rubyto ensure you're using the latest version - Always test in the sandbox environment first before moving to production
- Double-check that your QBO app has the
Attachmentsscope enabled in the Intuit Developer Portal
内容的提问来源于stack exchange,提问作者naina sharma

