HttpError 400 when trying to upload pdf file on Go

2019-08-14 16:37发布

I am setting up a service that:

  1. receives an email with an attachment file
  2. uploads this file to cloud storage
  3. uses that file as a source for further processing

I arrived at step 2 where the error is occurring. I am using the discovery API for Google services to authenticate. Mine is a simple flask application and below is the incoming email handler.

handle_incoming_email.py

__author__ = 'ciacicode'

import logging
import webapp2
from google.appengine.ext.webapp.mail_handlers import InboundMailHandler
from oauth2client.client import GoogleCredentials
from googleapiclient.discovery import build

credentials = GoogleCredentials.get_application_default()


class LogSenderHandler(InboundMailHandler):
    def receive(self, mail_message):
        logging.info("Received a message from: " + mail_message.sender)
        # upload attachment to cloud storage
        filename = 'any'
        try:
            attachment = mail_message.attachments
            filename = attachment[0].filename
            body = str(attachment[0].payload)
        except IndexError:
            print len(attachment)

        storage = build('storage', 'v1', credentials=credentials)
        req = storage.objects().insert(bucket='ocadopdf', body={'body': body, 'contentType': 'application/pdf', 'name': str(filename), 'contentEncoding': 'base64'})
        resp = req.execute()
        return resp


app = webapp2.WSGIApplication([LogSenderHandler.mapping()], debug=True)

After I send an email to the service I see the following error in the logs:

HttpError: https://www.googleapis.com/storage/v1/b/ocadopdf/o?alt=json returned "Upload requests must include an uploadType URL parameter and a URL path beginning with /upload/">

I understand that the information is not POSTed to the right endpoint and that is missing a URL parameter, but the more I dig in the Google documentation the less I can find something clear on how to use .build from the discovery module to add the URL path before the service 'storage'.

-- Update with solution --

    file = cStringIO.StringIO()
    file.write(body)
    media = http.MediaIoBaseUpload(file, mimetype='application/pdf')
    storage = build('storage', 'v1', credentials=credentials)
    req = storage.objects().insert(bucket='ocadopdf', body={'name': str(filename), 'contentEncoding': 'base64'}, media_body=media)
    resp = req.execute()
    return resp`

1条回答
啃猪蹄的小仙女
2楼-- · 2019-08-14 17:04

Your build invocation looks correct to me. This example provides a good reference on how to upload a file to GCS as a new object. Instead of passing a file handle to MediaIoBaseUpload, you can pass a io.BytesIO containing the object contents as described in the MediaIoBaseUpload documentation

查看更多
登录 后发表回答