How to convert Bytes object to _io.BytesIO python? - python

I am making a simple flask API for uploading an image and do some progresses then store it in the data base as binary, then i want to download it by using send_file() function but, when i am passing an image like a bytes it gives me an error:
return send_file(BytesIO.read(image.data), attachment_filename='f.jpg', as_attachment=True) TypeError: descriptor
'read' requires a '_io.BytesIO' object but received a 'bytes'
and My code for upload an image as follow:
#app.route('/upload', methods=['POST'])
def upload():
images = request.files.getlist('uploadImages')
n = 0
for image in images:
fileName = image.filename.split('.')[0]
fileFormat = image.filename.split('.')[1]
imageRead = image.read()
img = BytesIO(imageRead)
with graph.as_default():
caption = generate_caption_from_file(img)
newImage = imageDescription(name=fileName, format=fileFormat, description=caption,
data=imageRead)
db.session.add(newImage)
db.session.commit()
n = n + 1
return str(n) + ' Image has been saved successfully'
And my code for downloading an image:
#app.route('/download/<int:id>')
def download(id):
image = imageDescription.query.get_or_404(id)
return send_file(BytesIO.read(image.data), attachment_filename='f.jpg', as_attachment=True)
any one can help please???

It seems you are confused io.BytesIO. Let's look at some examples of using BytesIO.
>>> from io import BytesIO
>>> inp_b = BytesIO(b'Hello World', )
>>> inp_b
<_io.BytesIO object at 0x7ff2a71ecb30>
>>> inp.read() # read the bytes stream for first time
b'Hello World'
>>> inp.read() # now it is positioned at the end so doesn't give anything.
b''
>>> inp.seek(0) # position it back to begin
>>> BytesIO.read(inp) # This is same as above and prints bytes stream
b'Hello World'
>>> inp.seek(0)
>>> inp.read(4) # Just read upto four bytes of stream.
>>> b'Hell'
This should give you an idea of how read on BytesIO works. I think what you need to do is this.
return send_file(
BytesIO(image.data),
mimetype='image/jpg',
as_attachment=True,
attachment_filename='f.jpg'
)

Related

Saving a pymupdf fitz object to s3 as a pdf

I am trying to crop a pdf and save it to s3 with same name using lambda. I am getting error on the data type being a fitz.fitz.page
import os
import json
import boto3
from urllib.parse import unquote_plus
import fitz, sys
from io import BytesIO
OUTPUT_BUCKET_NAME = os.environ["OUTPUT_BUCKET_NAME"]
OUTPUT_S3_PREFIX = os.environ["OUTPUT_S3_PREFIX"]
SNS_TOPIC_ARN = os.environ["SNS_TOPIC_ARN"]
SNS_ROLE_ARN = os.environ["SNS_ROLE_ARN"]
def lambda_handler(event, context):
textract = boto3.client("textract")
if event:
file_obj = event["Records"][0]
bucketname = str(file_obj["s3"]["bucket"]["name"])
filename = unquote_plus(str(file_obj["s3"]["object"]["key"]))
doc = fitz.open()
s3 = boto3.resource('s3')
obj = s3.Object(bucketname, filename)
fs = obj.get()['Body'].read()
pdf=fitz.open("pdf", stream=BytesIO(fs))
#pdf.close()
rect=fitz.Rect(0.0, 0.0, 595.0, 842.0)
#page = pdf[0]
page1 = doc.new_page(width = rect.width, # new page with ...
height = rect.height)
page1.show_pdf_page(rect, pdf, 0)
print(type(doc))
print(type(page1))
s3.Bucket(bucketname).put_object(Key=filename, Body=page1)
This is happening because the page1 object is defined using fitz.fitz.page and the type expected by S3 put object is bytes.
In order to solve the issue, you can use the write function of the new PDF (doc) and get the output of it which is in bytes format that you could pass to S3 then.
# Save fil first.
new_bytes = doc.write()
s3.Bucket(bucketname).put_object(Key=filename, Body=new_bytes)
For some reason for me the doc.write() method didn't return a bytes object like stated above. Here's an additional way to create a new doc, convert to bytes using BytesIO, and then save it to s3 as a pdf:
import fitz
from io import BytesIO
client = boto3.client("s3")
# create new doc object
single_page = fitz.open()
# insert a page from original_pdf_doc
single_page.insert_pdf(
original_pdf_doc, from_page=from_page_num, to_page=to_page_num
)
# Use BytesIO and .write() method to save to a bytes object
bytes_ = BytesIO(single_page.write())
# Upload the bytes object!
client.put_object(Body=bytes_, Bucket=bucket, Key=key)

How can I return an image generated in pillow in django without saving in db?

def build_image(image_data):
template = Image.open("path/to/file/template.jpg")
draw = ImageDraw.Draw(template)
draw.text("(553,431)", f"{image_data.text}", fill=4324234, font=font)
file = InMemoryUploadedFile(template, None, 'result.jpg', 'image/jpeg', template.tell, None)
return FileResponse(file, as_attachment=True, filename='result.jpg')
I need to return the image that was modified, without saving it to the database. The above code gives the following error:
A server error occurred. Please contact the administrator.
I also tried the following option:
rea_response = HttpResponse(template, content_type='image/png')
rea_response['Content-Disposition'] = 'attachment; filename={}'.format('result.png')
return rea_response
But in this case, I get a corrupted file. (This format is not supported).
django==3.0.6
If you want to return raw bytes (of the image), then pass a file handler to Image.save() as said here.
E.g.
import io
def foo():
template = Image.open("path/to/file/template.jpg")
draw = ImageDraw.Draw(template)
draw.text("(553,431)", f"{image_data.text}", fill=4324234, font=font)
buff = io.BytesIO()
template.save(buff, format='JPEG')
return buff

Can't save changes to BytesIO buffer

While I am learning Flask, I wrote a small service that receives a image, resize and reduce it's quality.
To avoid write it to the disk and then delete it, I use a buffer, and it worked fine. But now I can't send it using flask send_file. I tried a few solutions that include wrap it using werkzeug FileWrapper and send using Response, but it also did not work. Also, it does't show any kind of error...
#app.route('/api/convert', methods=['POST'])
def convert():
if 'image' not in request.files:
return 'No image!'
if request.files['image'].content_type not in ALLOWED_CONTENT:
return 'Not an allowed image!'
filename = request.files['image'].filename
mimetype = request.files['image'].mimetype
print(mimetype)
buffer = io.BytesIO()
request.files['image'].save(buffer)
image = Image.open(buffer)
w, h = resize_image(*image.size)
image = image.resize((w, h), Image.ANTIALIAS)
print(type(buffer))
image.save(buffer,
format=mimetype.split('/')[-1],
optimize=True,
quality=DEFAULT_QUALITY)
return send_file(buffer,
mimetype=mimetype,
attachment_filename=filename,
as_attachment=True)
When I point to a file that exist in my system, it works fine...
UPDATE
It was pointed out that I was not using buffer.seek(0), after doing putting it on, i started to receive the image in my requests, but the image is far from what I expected.
For example, my test image is 5.2MB, when I save it to the disk instead of the buffer, it goes to 250KB, but when i try to save it to the buffer, and send it using send_file, it goes to 5.5MB...
#app.route('/api/convert', methods=['POST'])
def convert():
if 'image' not in request.files:
return 'No image!'
if request.files['image'].content_type not in ALLOWED_CONTENT:
return 'Not an allowed image!'
filename = request.files['image'].filename
mimetype = request.files['image'].mimetype
buffer = io.BytesIO()
request.files['image'].save(buffer)
image = Image.open(buffer)
w, h = resize_image(*image.size)
buffer.seek(0)
image = image.resize((w, h), Image.ANTIALIAS)
image.save(buffer,
format=mimetype.split('/')[-1],
optimize=True,
quality=DEFAULT_QUALITY)
buffer.seek(0)
return send_file(buffer,
mimetype=mimetype,
attachment_filename=filename,
as_attachment=True)
I am editing this question title and removing the tags for flask because it seems that my problem is just the lack of knowledge about io's BytesIO library.
UPDATE 2
I was working in another project when it came to my mind. What if I create a new buffer to save the image already modified?
And it worked.
#app.route('/api/convert', methods=['POST'])
def convert():
if 'image' not in request.files:
return 'No image!'
if request.files['image'].content_type not in ALLOWED_CONTENT:
return 'Not an allowed image!'
filename = request.files['image'].filename
mimetype = request.files['image'].mimetype
buffer = io.BytesIO()
buffer_final = io.BytesIO()
request.files['image'].save(buffer)
image = Image.open(buffer)
w, h = resize_image(*image.size)
image = image.resize((w, h), Image.ANTIALIAS)
image.save(buffer_final,
format=mimetype.split('/')[-1],
optimize=True,
quality=75)
buffer_final.seek(0)
return send_file(buffer_final,
mimetype=mimetype,
attachment_filename=filename,
as_attachment=True)
So, apparently I can't replace the content of the BytesIO buffer? Anyone knows what I am doing wrong? (yeah, I made it work, but I guess that other people would benefit from the same problem?)
Tested on my machine and truncate() after save(...) works just fine.
import math
import shutil
from PIL import Image
from io import BytesIO
src = r"C:\Users\justin\Desktop\test.jpg"
f = open(src, 'rb')
buffer = BytesIO()
shutil.copyfileobj(f, buffer)
print(f.tell(), buffer.tell())
f.close()
buffer.seek(0)
image = Image.open(buffer)
image.show()
print(buffer.tell())
print(image.size)
w, h = image.size
w, h = math.floor(w*0.75), math.floor(h*0.75)
print(w, h)
smaller = image.resize((w, h), Image.ANTIALIAS)
smaller.show()
print(smaller.size)
buffer.seek(0)
smaller.save(buffer, format='JPEG', optimize=True)
print(buffer.tell())
buffer.truncate()
buffer.seek(0)
out = Image.open(buffer)
out.show()
print(out.size)

Receiving an image with Fast API, processing it with cv2 then returning it

I am trying to build an API which receives an image and does some basic processing on it, then returns an updated copy of it using Open CV and Fast API. So far, I have the receiver working just fine, but when I try to base64 encode the processed image and send it back my mobile front end times out.
As a debugging practice I've tried just printing the encoded string and making the API call using Insomnia, but after 5 solid minutes of printing data I killed the application. Is returning a base64 encoded string the right move here? Is there an easier way to send an Open CV image via Fast API?
class Analyzer(BaseModel):
filename: str
img_dimensions: str
encoded_img: str
#app.post("/analyze", response_model=Analyzer)
async def analyze_route(file: UploadFile = File(...)):
contents = await file.read()
nparr = np.fromstring(contents, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
img_dimensions = str(img.shape)
return_img = processImage(img)
encoded_img = base64.b64encode(return_img)
return{
'filename': file.filename,
'dimensions': img_dimensions,
'encoded_img': endcoded_img,
}
#ZdaR 's comment did it for me. I was able to get the API call to work by re-encoding it to a PNG prior to encoding it to a base64 string.
The working code is as follows:
class Analyzer(BaseModel):
filename: str
img_dimensions: str
encoded_img: str
#app.post("/analyze", response_model=Analyzer)
async def analyze_route(file: UploadFile = File(...)):
contents = await file.read()
nparr = np.fromstring(contents, np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
img_dimensions = str(img.shape)
return_img = processImage(img)
# line that fixed it
_, encoded_img = cv2.imencode('.PNG', return_img)
encoded_img = base64.b64encode(encoded_img)
return{
'filename': file.filename,
'dimensions': img_dimensions,
'encoded_img': endcoded_img,
}

Decode base64 images using a loop

I have an unknown number of images which were encoded using base64. They are in the following format:
image0 = """\ iVBORw0KGgoAAAANSUhEUgAAABcAAAATCAIAAAD02QHhAAAAB3RJTUUH3gMEFjA2JHVG0AAAAe5JREFUeJztUzGr6jAYTZoWVAQRKTooOIja0SK4da6DkyCIi39AN531DzgKIoroLoiDk3ZWB3UoCiIqFDq5OBRKaXKHXOLjPnn3Xd76zpYvyTnfd3ICCSHgn8G9rf6UmieEQAgJIRjjFzfHUS4I4RdeWqRX2C78syzTYEsqgDH+lYvHGHMc93g8DoeD1+t1HMc0Tb/fr6oqlWJHKY7HYyKREAThswsIAQCc67qEEF3Xd7tdo9GYTqeWZWUyGYQQFaD6EELbtjVNq1ar2+12u90+Hg82KS8IAiFEURRFUfb7fbvdDgQCAIDZbHY+nwVBqNVqk8lkvV63Wq3RaBSNRk+nk23bsVjs5R3G2HVd13Xv93upVHIcx3Gc+XzebDY1TSsUCoSQdDrd7XYJIeVyebVakd/AQwhp59frNRKJ8DwPAFgsFj6f73a79Xq94XBYr9eXy6Xf7z+fz7IsY4xt2/Z4PMyal+GbzSYej1PucDj8fD4lSer3+8FgMJVK6bqeTCaz2Wyn0xmPx9RN5i5qtVo0IJZlybIsiiIAIJfLGYZhmmalUkEIDQaDfD6vqqokSZfLpVgshkIh9nDf54X6ZxiGKIpshC+B/GShJZpdllrXdQEACCHWNj2JMUYI/Sy77DJr/i2+Z/kbvP/T/1k+AL7rRN+R/gD6AAAAAElFTkSuQmCC """
image1 = """\ iVBORw0KGgoAAAANSUhEUgAAABcAAAATCAIAAAD02QHhAAAAB3RJTUUH3gMEFjE42tZalgAAAoRJREFUeJyNUz1PKlEQnbl3YzTYEBshodpGo62hIdgaExsLKiojP4DChD9AI50/wGggsSAhivEnYAKJ0kEAjRUaCODKx25C9t6xGFzfU3nPKe7encycPXPmLBIRAPDpuu7V1dV4PAYApZRlWQcHB36/HwC01oiIiESEiLVajYg2NjYAAAAEPxjFtm2t9cPDQzabDQQCe3t7fr9/PB4Ph0MhBCJalmXb9u3tbSKRuLi4uLy8LJfLRGTMwIQAgOXl5VgsNplMQqHQ7u4uAJRKpfv7+2azeXh4aJrm9vZ2oVAYDoeIGIlEbNv2+Xx/cSEirbVS6u7uzjRNpdRgMMhkMtFotNPprKys5HI5RDRNs9VqhcPhnZ2d/f39zc1NRJyhICIAGIYhpXx9fV1bW5NSPj4+DgaDRqORTqcrlcrq6urW1tbR0VGpVFpfXweATqejtSaiGQrTQcR2u+04TjAY1FqHQqGFhQUiur6+Ho1Gi4uLhmFMp9NkMnl2dnZ8fNzr9Vj42Sw8jta63++zYK7rElGz2cxms41Gg4hSqVQ6na5Wq0RULBbr9ToRKaWICHk7X4J58ckZ13UNw3AcZ2lpyct7l08Ur1NrLaXkV6UU79grYON49mFNP3XxUlJKvhCRlHIymdzc3Dw9Pf3ZLKVkdO4SMCdY/F6vd3Jywn5pt9sM/UM1zQlGeXt76/f7RBSPx5+fn9lQ34uNeVyYqs/nk1Ken59Ho9FAIOC6Ls/7JeZOBABKKSllrVYDgEQiwTJ5W/s/ChtBCJHP51Op1HQ6PT09dRwHPv7b33Jh/bvdbiQSEUK8vLxYljUPZY7mH4x+5P89flaXodl13mc8Xb9D/4vL76m9A4fr1aGVlymDAAAAAElFTkSuQmCC """
image2 = """\ iVBORw0KGgoAAAANSUhEUgAAABcAAAATCAIAAAD02QHhAAAAB3RJTUUH3gMEFjE42tZalgAAAoJJREFUeJyVUj1La0EQndm9+BUJhqRQIdVtDIZ0wUa0ExFsLPwBYho7i0D+QArjfxAhgtiIH5U/IIKCRkEiSUxpccHchBhvINydsdiw5uUl8t4pdmeHnbNnzg4yMwDo1ff9y8vLdrsNAEqpZrO5s7MTCoUAgIgQERGZGRFLpRIzLy4uAgAACL1pFs/ziOjt7S2fz8/NzW1uboZCoXa73Wq1hBCI2Gw2Pc+7vb1NpVKnp6cXFxd3d3fMbPXIhACA6enp7e3tr6+vaDS6sbEBAIVC4fHxsVKp7O7u2ra9urp6fn7earUQcXl52fO8QCDwhxZmJiKl1MPDg23bSinXdQ8PD1dWVhzHCYfDJycniGjbdrVaXVpaWl9f39raisfjiNhjQUQAsCxLStloNBYWFqSUtVrNdd1yuZzNZu/v72dnZ5PJZDqdLhQKsVgMABzHISJm7rFoOYj4/v7e6XTm5+eJKBqNjo2NMfP19fXn5+fExIRlWd1ud39///j4OJfLfXx8aON7veh2iKher2vDfN9n5kqlks/ny+UyM2cymWw2WywWmfnq6ur19ZWZlVLMjPp3BqB16VVnfN+3LKvT6UxOTpq8CX5YTCURSSn10YyJuaAz/fkfd43BiCil1IEu8zzv5uamVCr1F0sp9QTpKgEjoEUBQK1Wcxxnb2/v6elJE/192RrFoh9k5kQikUgknp+fx8fHjeQB/KZFKUVEZ2dnyWQyEonEYjHd0fDbo6Anqtvtuq67trZWrVbN1w5gZEdEJIR4eXkJBoONRmNqampmZmaoKb/5Ypo6OjoSQhwcHEQikVEdDZ86w9JfM3D8Jy1mxvQziCiEMPF/aDF0Qyv78Q28N9fZLnpYLAAAAABJRU5ErkJggg== """
I can successfully decode them using:
imageToShow = base64.b64decode(image0)
stream1 = cStringIO.StringIO(imageToShow)
bmp1 = wx.ImageFromStream(stream1)
self.image_spot0.SetBitmap(wx.BitmapFromImage(bmp1))
imageToShow = base64.b64decode(image1)
stream1 = cStringIO.StringIO(imageToShow)
bmp1 = wx.ImageFromStream(stream1)
self.image_spot1.SetBitmap(wx.BitmapFromImage(bmp1))
imageToShow = base64.b64decode(image2)
stream1 = cStringIO.StringIO(imageToShow)
bmp1 = wx.ImageFromStream(stream1)
self.image_spot2.SetBitmap(wx.BitmapFromImage(bmp1))
Since there could be 3, 100, 300 or hey why not 5000 encoded images, I would like to be able to process the decoding using a loop.
When I attempted using the following code:
image_group = ['image0', 'image1', 'image2']
for img in image_group:
imageToShow = base64.b64decode(img)
I get a padding error, I assume that the data is not passing to base64.b64decode(), only the actually string "image0". Any help would be appreciated.
You mixed up variable name string literal with same value.
The solution is in writing:
image_group = ['image0', 'image1', 'image2']
You can even reduce it by combining declarations like:
image_group = [
"iVBORw0KGgoAAAANSUhEUgAAA...",
"iVBORw0KGgoAAAANSUhEUgAAA...",
"iVBORw0KGgoAAAANSUhEUgAAA..."
]

Categories

Resources