Skip to content

MediaUpload with has_stream() = False and an empty last chunk results in HTTP Error 400 #1801

Open
@john0312

Description

@john0312

Environment details

  • OS type and version: Ubuntu 20.04
  • Python version: Python 3.8.10
  • pip version: pip 20.0.2
  • google-api-python-client version: Version: 2.46.0

Steps to reproduce

  1. Create a class that inherit off MediaUpload, but resumable() = True and has_stream() = False so getbytes() will be used.
  2. Do an upload that is an exact multiples of the chunksize.
  3. The last chunk will be empty, and the header will look like this:
{'Content-Length': '0', 'Content-Range': 'bytes 268435456-268435455/268435456'}
  1. This will yield the HTTP 400 error code.

Code example

Note: Please fill in a folder ID in metadata['parents'] before running.

from __future__ import print_function

import os.path

from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from googleapiclient.http import MediaUpload

# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive.file", "https://www.googleapis.com/auth/drive.metadata.readonly"]

class ExampleUpload(MediaUpload):
  def __init__(self):
    self.data = b'\0'*(200*1024*1024)

  def chunksize(self):
    return 100*1024*1024

  def mimetype(self):
    return "application/octet-stream"

  def size(self):
    return None

  def resumable(self):
    return True

  def has_stream(self):
    return False

  def getbytes(self, begin, length):
    length = min(len(self.data)-begin, length)
    return self.data[begin:begin+length]

def main():
    """Shows basic usage of the Drive v3 API.
    Prints the names and ids of the first 10 files the user has access to.
    """
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.json'):
        creds = Credentials.from_authorized_user_file('token.json', SCOPES)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.json', 'w') as token:
            token.write(creds.to_json())

    try:
        service = build('drive', 'v3', credentials=creds)

        metadata = {}
        metadata['name'] = 'test_file1'
        metadata['parents'] = <FOLDER ID HERE>
        media_body = ExampleUpload()
        request = service.files().create(body=metadata, media_body=media_body)
        response = None
        while response is None:
          status, response = request.next_chunk()
          print('Got status and response: %s %s'%(status, response))
    except HttpError as error:
        # TODO(developer) - Handle errors from drive API.
        print(f'An error occurred: {error}')


if __name__ == '__main__':
    main()

Stack trace

  File "/venv/lib/python3.8/site-packages/googleapiclient/_helpers.py", line 131, in positional_wrapper
    return wrapped(*args, **kwargs)
  File "/venv/lib/python3.8/site-packages/googleapiclient/http.py", line 1097, in next_chunk
    return self._process_response(resp, content)
  File "/venv/lib/python3.8/site-packages/googleapiclient/http.py", line 1128, in _process_response
    raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://www.googleapis.com/upload/drive/v3/files?alt=json&uploadType=resumable returned "Bad Request". Details: "Failed to parse Content-Range header.">

Metadata

Metadata

Assignees

No one assigned

    Labels

    priority: p2Moderately-important priority. Fix may not be included in next release.type: bugError or flaw in code with unintended results or allowing sub-optimal usage patterns.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions