Issue with streaming data from zip to files.upload

I’m working on a demo that will do a prompt based on images found within a zip file. My logic basically boils down to:

Take the zip and find all images in it
For each image, I read in the bits from zip as a variable, bits, and then upload it, adding the result files object to an array

When its time to do my prompt, I simply prepend the array of file objs w/ a hard coded prompt and fire it off.

I keep getting vauge 400 errors about the request containing invalid arguments. From what I can see, each file upload works just fine, but there’s something off because if I modify my code to extract and save the image to the file system, using a temporary name, and then upload that, it works. I’d rather avoid saving the images. Does anyone see anything wrong with how I’m getting the data? Here’s a snippet:

gemini_files = []

if comic.endswith("cbz"):
	with zipfile.ZipFile(os.path.join(comic_dir,comic),'r') as zip:
		files = zip.namelist()
		# todo - check and see if we need more image extensions
		images = [file for file in files if (file.endswith("jpg"))][0:3]
		counter = 0
		for image in images:
			with zip.open(image, 'r') as imgbin:
				print('read binary for',image)
				bits = io.BytesIO(imgbin.read())
				gem_file = client.files.upload(file=bits, config={"mime_type":"image/jpg"})
				gemini_files.append(gem_file)

Note the [0:3] after the list comprehension is just a shortcut to limit the number of images while I test. If I switch my code as described:

counter = counter + 1
with open(f'temp{counter}.jpg', 'wb') as file:
	file.write(imgbin.read())
	gemini_files.append(client.files.upload(file=f'temp{counter}.jpg'))

It works.

@raymondcamden,

welcome to the community.

I see you are sending bytes of image data as it is and that might probably be ther reason why you are getting the error.

please consider chnaging those input image bytes to a base 64 strign adn use that instead. I believe this will get you the right response.

Switching my input to b64 makes the upload call fail completely as it thinks it’s a path instead.

@raymondcamden

i just realized you are using files api not directly giving bytes in the input context.
files api will take the file path as input and your are giving bytes of data instead!

in the second piece of code, you are saving it as temp file and givignt hat path and it works !

Actually, the Files API also supports BytesIO objects. I figured out my issue and it was completely stupid - I was using a mimetype of imag/jpg, not image/jpeg. Sigh.

This works:

if comic.endswith("cbz"):
	with zipfile.ZipFile(os.path.join(comic_dir,comic),'r') as zip:
		files = zip.namelist()
		# todo - check and see if we need more image extensions
		images = [file for file in files if (file.endswith("jpg"))][0:3]
		for image in images:
			with zip.open(image, 'r') as imgbin:
				print('read binary for',image)
				gemini_files.append(client.files.upload(file=io.BytesIO(imgbin.read()), config={"mime_type":"image/jpeg"}))


	prompt_contents = [prompt] + gemini_files
	response = client.models.generate_content(model="gemini-2.5-flash", contents=prompt_contents)
	print(response.text)

But isn’t it would take too much time cause there are many models of Gemini