Im trying to read in image file from a server , with the code below . It keeps going into the exception. I know the correct number of bytes are being sent as I print them out when received. Im sending the image file from python like so
#open the image file and read it into an object
imgfile = open (marked_image, 'rb')
obj = imgfile.read()
#get the no of bytes in the image and convert it to a string
bytes = str(len(obj))
#send the number of bytes
self.conn.send( bytes + '\n')
if self.conn.sendall(obj) == None:
imgfile.flush()
imgfile.close()
print 'Image Sent'
else:
print 'Error'
Here is the android part , this is where I'm having the problem. Any suggestions on the best way to go about receiving the image and writing it to a file ?
//read the number of bytes in the image
String noOfBytes = in.readLine();
Toast.makeText(this, noOfBytes, 5).show();
byte bytes [] = new byte [Integer.parseInt(noOfBytes)];
//create a file to store the retrieved image
File photo = new File(Environment.getExternalStorageDirectory(), "PostKey.jpg");
DataInputStream dis = new DataInputStream(link.getInputStream());
try{
os =new FileOutputStream(photo);
byte buf[]=new byte[1024];
int len;
while((len=dis.read(buf))>0)
os.write(buf,0,len);
Toast.makeText(this, "File recieved", 5).show();
os.close();
dis.close();
}catch(IOException e){
Toast.makeText(this, "An IO Error Occured", 5).show();
}
EDIT: I still cant seem to get it working. I have been at it since and the result of all my efforts have either resulted in a file that is not the full size or else the app crashing. I know the file is not corrupt before sending server side. As far as I can tell its definitely sending too as the send all method in python sends all or throws an exception in the event of an error and so far it has never thrown an exception. So the client side is messed up . I have to send the file from the server so I cant use the suggestion suggested by Brian .
The best way to get a bitmap from a server is to execute the following.
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet("http://yoururl");
HttpResponse response = client.execute(get);
InputStream is = response.getEntity().getContent();
Bitmap image = BitmapFactory.decodeStream(is);
That will give you your bitmap, to save it to a file do something like the following.
FileOutputStream fos = new FileOutputStream("yourfilename");
image.compress(CompressFormat.PNG, 1, fos);
fos.close();
You can also combine the two and just write straight to disk
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet("http://yoururl");
HttpResponse response = client.execute(get);
InputStream is = response.getEntity().getContent();
FileOutputStream fos = new FileOutputStream("yourfilename");
byte[] buffer = new byte[256];
int read = is.read(buffer);
while(read != -1){
fos.write(buffer, 0, read);
read = is.read(buffer);
}
fos.close();
is.close();
Hope this helps;
I'm not sure I understand your code. You are calling dis.readFully(bytes); to write the content of dis into your byte array. But then you don't do anything with the array, and then try to write the content of dis through a buffer into your FileOutputStream.
Try commenting out the line dis.readFully(bytes);.
As a side note, I would write to the log rather than popping up a toast for things like the number of bytes or when an exception occurs:
...
} catch (IOException e) {
Log.e("MyTagName","Exception caught " + e.toString());
e.printStackTrace();
}
You could look at these links for examples of writing a file to the SD card:
Android download binary file problems
Android write to sd card folder
I solved it with the help of a Ubuntu Forums member. It was the reading of the bytes that was the problem . It was cutting some of the bytes from the image. The solution was to just send the image whole and remove the sending of the bytes from the equation all together
Related
I have a post request in Flask that accepts an image file, and I want to return another image to retrieve it in Flutter and put it on screen.
In Flutter, I can send the image through the post request, but I don't know how to retrieve an image and put it on screen.
I know I can save the image in the static folder at Flask, and retrieve the URL from Flutter, and it works, but I think this is too inefficient for what I'm doing.
So I want to send the image directly without saving it.
This was my last attempt but didn't work.
#app.route("/send-image", methods=['POST'])
def send_image():
if request.method == 'POST':
user_image = request.files["image"]
image = cv2.imdecode(np.frombuffer(
user_image.read(), np.uint8), cv2.IMREAD_COLOR)
#data is a NumPy array returned by the predict function. This numpy array it's an image
data = predict(image)
data_object = {}
data = data.reshape(data.shape[0], data.shape[1], 1)
data2 = array_to_img(data)
b = BytesIO()
data2.save(b, format="jpeg")
b.seek(0)
data_object["img"] = str(b.read())
return json.dumps(data_object)
Here I returned a Uint8List because I read from the internet that I can put that into an Image.memory() to put the image on the screen.
Future<Uint8List> makePrediction(File photo) async {
const url = "http://192.168.0.11:5000/send-image";
try {
FormData data = new FormData.fromMap({
"image": await MultipartFile.fromFile(photo.path),
});
final response = await dio.post(url, data: data);
String jsonResponse = json.decode(response.data)["img"].toString();
List<int> bytes =
utf8.encode(jsonResponse.substring(2, jsonResponse.length - 1));
Uint8List dataResponse = Uint8List.fromList(bytes);
return dataResponse;
} catch (error) {
print("ERRORRR: " + error.toString());
}
}
Sorry if what I did here doesn't make sense, but after trying a lot of things I wasn't thinking properly.
I really need your help
You can convert the image to base64 and display it with Flutter.
On server:
import base64
...
data_object["img"] = base64.b64encode(b.read()).decode('ascii')
...
On client:
...
String imageStr = json.decode(response.data)["img"].toString();
Image.memory(base64Decode(imageStr));
...
The problem with your server-side code is it tries to coerce a bytes to str object by using function str().
However, in Python 3, bytes.__repr__ is called by str() since bytes.__str__ is not defined. This results in something like this:
str(b'\xf9\xf3') == "b'\\xf9\\xf3'"
It makes the JSON response looks like:
{"img": "b'\\xf9\\xf3'"}
Without writing a dedicated parser, you can not read this format of image data in Flutter. However, base64 is a well known format of encoding binary data and we do have a parser base64Decode in Flutter.
I'm trying to retrieve a picture over an inputstream and then set my ImageView to that picture. Below is my code for how to retrieve the inputstream and read the data.
let bufferSize = 1024
var buffer = Array<UInt8>(repeating: 0, count: bufferSize)
while (inputStream.hasBytesAvailable){
let len = inputStream.read(&buffer, maxLength: buffer.count)
if(len > 0){
let imageData = NSData(bytes: &buffer, length: buffer.count)
print("\(imageData)")
let newImage = UIImage(data: imageData as Data)
ImageView.image = newImage
}
}
I'm using the CFStream as sockets for receiving data. All of this works fine as I'm able to send and receive all my other data but for some reason the picture doesn't.
Many thanks.
UPDATE.
The code from below doesn't throw any errors but the picture is still not showing. Is there anything wrong with my server code?
f = open("/home/pi/Documents/server/foo.jpg", "rb")
byte = f.read()
print "Done reading"
self.transport.write(byte)
f.close()
print "Done sending"
The server code is written in Twisted Python and the connection is working accordingly since I'm able to transfer other messages than the picture.
So, to accumulate the data fragment into one Data, you need to write something like this:
let bufferSize = 1024
var buffer = Array<UInt8>(repeating: 0, count: bufferSize)
//You need to assign an actual Data to the variable.
//With initial value given, you have no need to work with Optionals.
var imageData = Data()
while (inputStream.hasBytesAvailable){
let len = inputStream.read(&buffer, maxLength: buffer.count)
if(len > 0){
imageData.append(&buffer, count: buffer.count)
}
}
//You need to try converting the data to image after reading all data fragments.
let newImage = UIImage(data: imageData)
pictureView.image = newImage
If you find other issues with this code, please report as comments to this answer.
My mistake was not sending each byte of the file. When i changed my server code to:
with open("/home/pi/Documents/server/foo.jpg", "rb") as f:
byte = f.read()
print "Done reading"
while byte:
self.transport.write(byte)
byte = f.read(1)
print "Done sending"
f.close()
It worked fine. However, I will accept the answer below because it might have been a problem with how I was reading my data into the new image file as well.
I have an iOS mobile application that sends an encoded image to a Python3 server.
static func prepareImageAndUpload(imageView: UIImageView) -> String?
{
if let image: UIImage? = imageView.image {
// You create a NSData from your image
let imageData = UIImageJPEGRepresentation(imageView.image!, 0.5)
// You create a base64 string
let base64String = imageData!.base64EncodedStringWithOptions([])
// And you encode it in order to delete any problem of specials char
let encodeImg = base64String.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) as String!
return encodeImg
}
return nil
}
And I am trying to receive that image using the following code:
imageName = "imageToSave.jpg"
fh = open(imageName, "wb")
imgDataBytes = bytes(imgData, encoding="ascii")
imgDataBytesDecoded = base64.b64decode(imgDataBytes)
fh.write(imgDataBytesDecoded)
fh.close()
I create the image file successfully and nothing breaks. And I can see that the filesize is correct, but the image is not correct, since it can't be opened and shows that it is broken.
I am not sure where the error can be, since the logic is as follows:
Encode image with base64 on iOS device
Send it
Decode image with base64 on Python3 server
Save image from decoded bytes
I have tried two new variants:
Remove stringByAddingPercentEncodingWithAllowedCharacters and
the result was the same
Add urldecode in Python3 server and the result was the same
I'm attempting to grab an image attached to an email using Jython 2.5.3. I get the email (using they Jython version of the Python imap library). I can get the attachment by looping through the parts, finding the correct part type using get_content_type():
image, img_ext = None, None
for part in self.mail.get_payload():
part_type, part_ext = part.get_content_type().split('/')
part_type = part_type.lower().strip()
part_ext = part_ext.lower().strip()
if part_type == 'image':
image = part.get_payload(decode=True)
img_ext = part_ext
return image, img_ext
'image' is returned as a big block of bytes, which in regular Python I'd write out directly to a file. However when I try the same thing in Jython I get the following error:
TypeError: write(): 1st arg can't be coerced to java.nio.ByteBuffer[], java.nio.ByteBuffer
What's the right way to make Jython recognize my big blob of data as a byte array?
PS: the writing code uses tempfile.mkstmp(), which defaults to writing binary...
For future readers, here's how I got around it. In the code tha does the writing:
from org.python.core.util import StringUtil
from java.nio import ByteBuffer
tmp, filename = tempfile.mkstemp(suffix = "." + extension, text=True)
bytes = StringUtil().toBytes(attachment)
bb = ByteBuffer.wrap(bytes)
tmp.write(bb)
tmp.close()
I have seen the receipes for uploading files via multipartform-data and pycurl. Both methods seem to require a file on disk. Can these recipes be modified to supply binary data from memory instead of from disk ? I guess I could just use a xmlrpc server instead.I wanted to get around having to encode and decode the binary data and send it raw... Do pycurl and mutlipartform-data work with raw data ?
This (small) library will take a file descriptor, and will do the HTTP POST operation: https://github.com/seisen/urllib2_file/
You can pass it a StringIO object (containing your in-memory data) as the file descriptor.
Find something that cas work with a file handle. Then simply pass a StringIO object instead of a real file descriptor.
I met similar issue today, after tried both and pycurl and multipart/form-data, I decide to read python httplib/urllib2 source code to find out, I did get one comparably good solution:
set Content-Length header(of the file) before doing post
pass a opened file when doing post
Here is the code:
import urllib2, os
image_path = "png\\01.png"
url = 'http://xx.oo.com/webserviceapi/postfile/'
length = os.path.getsize(image_path)
png_data = open(image_path, "rb")
request = urllib2.Request(url, data=png_data)
request.add_header('Cache-Control', 'no-cache')
request.add_header('Content-Length', '%d' % length)
request.add_header('Content-Type', 'image/png')
res = urllib2.urlopen(request).read().strip()
return res
see my blog post: http://www.2maomao.com/blog/python-http-post-a-binary-file-using-urllib2/
Following python code works reliable on 2.6.x. The input data is of type str.
Note that the server that receives the data has to loop to read all the data as the large
data sizes will be chunked. Also attached java code snippet to read the chunked data.
def post(self, url, data):
self.curl = pycurl.Curl()
self.response_headers = StringIO.StringIO()
self.response_body = io.BytesIO()
self.curl.setopt(pycurl.WRITEFUNCTION, self.response_body.write)
self.curl.setopt(pycurl.HEADERFUNCTION, self.response_headers.write)
self.curl.setopt(pycurl.FOLLOWLOCATION, 1)
self.curl.setopt(pycurl.MAXREDIRS, 5)
self.curl.setopt(pycurl.TIMEOUT, 60)
self.curl.setopt(pycurl.ENCODING,"deflate, gzip")
self.curl.setopt(pycurl.URL, url)
self.curl.setopt(pycurl.VERBOSE, 1)
self.curl.setopt(pycurl.POST,1)
self.curl.setopt(pycurl.POSTFIELDS,data)
self.curl.setopt(pycurl.HTTPHEADER, [ "Content-Type: octate-stream" ])
self.curl.setopt(pycurl.POSTFIELDSIZE, len(data))
self.curl.perform()
return url, self.curl.getinfo(pycurl.RESPONSE_CODE),self.response_headers.getvalue(), self.response_body.getvalue()
Java code for the servlet engine:
int postSize = Integer.parseInt(req.getHeader("Content-Length"));
results = new byte[postSize];
int read = 0;
while(read < postSize) {
int n = req.getInputStream().read(results);
if (n < 0) break;
read += n;
}
found a solution that works with the cherrypy file upload example: urllib2-binary-upload.py
import io # Part of core Python
import requests # Install via: 'pip install requests'
# Get the data in bytes. I got it via:
# with open("smile.gif", "rb") as fp: data = fp.read()
data = b"GIF89a\x12\x00\x12\x00\x80\x01\x00\x00\x00\x00\xff\x00\x00!\xf9\x04\x01\n\x00\x01\x00,\x00\x00\x00\x00\x12\x00\x12\x00\x00\x024\x84\x8f\x10\xcba\x8b\xd8ko6\xa8\xa0\xb3Wo\xde9X\x18*\x15x\x99\xd9'\xad\x1b\xe5r(9\xd2\x9d\xe9\xdd\xde\xea\xe6<,\xa3\xd8r\xb5[\xaf\x05\x1b~\x8e\x81\x02\x00;"
# Hookbin is similar to requestbin - just a neat little service
# which helps you to understand which queries are sent
url = 'https://hookb.in/je2rEl733Yt9dlMMdodB'
in_memory_file = io.BytesIO(data)
response = requests.post(url, files=(("smile.gif", in_memory_file),))