What i'm trying to do is transfer an image from the web service to the mobile client. In order to do this i've created a web service operation that returns a byte[] variable. In this method i create an .png image from a chart. After this i get the bytes from the image and provide them as a return value for the operation. this is the server code:
public byte[] getBytes() throws IOException {
BufferedImage chartImage = chart.createBufferedImage(230, 260);
//I get the image from a chart component.
ByteArrayOutputStream baos = new ByteArrayOutputStream(1000);
ImageIO.write( chartImage, "png",baos );
baos.flush();
byte[] bytesImage = baos.toByteArray();
baos.close();
return bytesImage;
}
Now in the mobile application all i do is assign a byte[] variable the return value of the web service operation.
byte[] imageBytes = Stub.getBytes().
Maybe i'm missing something but this is not working as i get this runtime error:
java.rmi.MarshalException: Expected Byte, received: iVBORw0KGgoAAAANSUhEU.... (very long line).
have any ideas why this happends? Or maybe you can suggest any other way to send the data to the mobile client.
If the service is only delivering an image as a byte array, the overhead induces by wrapping this in a SOAP response and XML/SOAP parsing on the client side seem rather unnecessary. Why don't you implement the chart generation in a servlet and let the client retrieve the image from a 'non-SOAP' server URL?
Instead of returning bytesImage from a WebService method like you do, you could instead write the byte array to the servlet's response object:
response.setContentType("image/png");
response.setContentLength(bytesImage.length);
OutputStream os = response.getOutputStream();
os.write(bytesImage);
os.close();
On the J2ME client, you would read the response from the URL, to which the servlet is bound and create an image from the data:
HttpConnection conn = (HttpConnection)Connector.open("http://<servlet-url>");
DataInputStream dis = conn.openDataInputStream();
byte[] buffer = new byte[conn.getLength()];
dis.readFully(buffer);
Image image = Image.createImage(buffer, 0, buffer.length);
Hope this helps!
Related
I'm trying to retrieve an image from a server location using Spring Boot to display in the front end.
I used a byte array conversion, but it takes a lot of time to retrieve. Is there any solution other than using byte array? Thank you!!!
byte[] data;
imagepath = "E:\\images\\";
InputStream inputStream = new BufferedInputStream(new FileInputStream(imagePath));
data= IOUtils.toByteArray(inputStream);
I'm a little confused around the issue of returning a byte array vs a stream in an HTTP Response using .net Web API.
I came across the following code:
SqlConnection conn = new SqlConnection();
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "Select FileData.PathName() As FilePath, GET_FILESTREAM_TRANSACTION_CONTEXT() AS Context From FileStorage";
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
reader.Read();
string filePath = (string)reader["FilePath"];
byte[] fileBytes = (byte[])reader["Context"];
SqlFileStream stream = new SqlFileStream(filePath, fileBytes, FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
Question 1:
Why would they return a Stream instead of a byte array in the HTTP Response?
Question 2:
Why create a SqlFileStream to read the data if the byte array is already available by calling (byte[])reader["Context"]? Wouldn't this mean that the entire file contents are read into memory? So why the need for a Stream?
Question 1: Why would they return a Stream instead of a byte array in the HTTP Response?
Because the byte array may be huge, so if you read the entire array into the memory of the server and keep it in memory until it has all been transmitted to the client you are imposing a huge memory burden on the server. That's the stuff Denial-Of-Service attacks are made of. By using a stream you allow the server to load the data in small chunks, on an as-needed basis, and to keep only a small chunk in memory at any given time, while waiting for it to be transmitted.
Question 2: Why create a SqlFileStream to read the data if the byte array is already available by calling (byte[])reader["Context"]? Wouldn't this mean that the entire file contents are read into memory? So why the need for a Stream?
The byte array that you see there is not the actual file contents. If you look at the documentation of the constructor of SqlFileStream, and also at the documentation of the SqlFileStream class, this byte array is some "transaction context" which is (a terrible hack) necessary for the database server to read the actual the data from storage. The actual data is potentially huge, so the code that you posted does all this in order to avoid loading it all into memory.
Buffering is the main reason for returning StreamContent. In ASP.NET Web API every time you return StreamContent, your response is not buffered however byte array response is already buffered and available to serve. In the case of byte[] the content of HttpResponseMessage could be set directly from your byte[] and you do not need to convert it to Stream type.
In addition consider using PushStreamContent in scenarios in which you want to stream binary contents to the client continuously so client can consume your api progressively as the data arrives similar to following code snipet:
var httpResponseMessage = new HttpResponseMessage
{
Content = new PushStreamContent(async (respStream, content, context) =>
{
using (var writer = new StreamWriter(respStream))
{
await writer.WriteLineAsync();
await writer.FlushAsync();
}
}, "text/plain")
};
Hope someone could please provide any help on an issue I'm struggling for quite a lot of time.
Goal: I need to read an http stream from a specified URI, that I startup & endlessly read with the following code (which I stripped down to minimum so to really focus on the bare communication problem):
public void StartupStream(Uri uri)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);
// Start the asynchronous request
request.BeginGetResponse(OnGetResponse, request);
}
private void OnGetResponse(IAsyncResult asyncResult)
{
// get the response
HttpWebRequest req = (HttpWebRequest)asyncResult.AsyncState;
try
{
using (HttpWebResponse resp = (HttpWebResponse)req.EndGetResponse(asyncResult))
{
using (Stream s = resp.GetResponseStream())
{
// dummy-read the stream forever
int readBytes = 0;
byte[] buffer = new byte[4096];
while (true)
{
readBytes += s.Read(buffer, 0, buffer.Length);
}
}
}
}
catch (Exception ex)
{
throw;
}
finally
{
req.Abort();
}
}
Issue: it happens that the above same exact code perfectly runs on a demo desktop WPF app, reading "gigabytes" of data without any issue, whereas on a Windows Phone 8.1 Store App I can only read up to 65536 bytes, and then the subsequent call to s.Read(buffer, 0, buffer.Length) (in the infinite while loop) just hangs forever, without any exception!
Things I have already tried, without any result:
Changing several values for the buffer size (i.e. 256, 512, 1024… and
so on)
Running the WP 8.1 app on both device and emulator
Sniffing traffic with WireShark, I can see the startup request is exactly the same on both WPF and WP 8.1 scenarios, and both are HTTP
1.1, and in all cases, the server (a D-link DCS-920 webcam) continues to flawlessly “pump” HTTP 1.1 responses (mjpeg data) into the stream
Does anybody have an idea of what could be going on with this bare, simple HTTP Stream usage on WP 8.1? How could there be a 65536-byte limitation on reads?
Thanks for any help!
This is how I am downloading an images from the web. The file size in the example is 1131683
string URL = "http://img.uuhy.com/uploads/2010/05/4423_Free-high-resolution-desktop-wallpaper-8.jpg";
var httpClient = new HttpClient();
var httpResponse = await httpClient.GetAsync(URL);
var ImageArray = await httpResponse.Content.ReadAsByteArrayAsync();
I'm using the Gmail API in browser and want to allow the user to download email attachments. I see https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get but it returns JSON and base64 data. I don't think I can get that data in memory then trigger a "download" to save the file locally. Even if I could I don't think it would be efficient - it would probably download the file in memory vs. streaming it to a file. I think I need a direct link to a file that returns the correct file name and raw binary data (not base64). Is there a way to do this? Right now the only way I see is to proxy requests.
You can get the data from the base64 and save it to file locally.
If you are getting the attachment in Java, you can use the FileOutputStream class (or f.write() in Python) to write the bytes to file and save it locally with a path.
You can try with the following sample code from Google Developer page:
public static void getAttachments(Gmail service, String userId, String messageId)
throws IOException {
Message message = service.users().messages().get(userId, messageId).execute();
List<MessagePart> parts = message.getPayload().getParts();
for (MessagePart part : parts) {
if (part.getFilename() != null && part.getFilename().length() > 0) {
String filename = part.getFilename();
String attId = part.getBody().getAttachmentId();
MessagePartBody attachPart = service.users().messages().attachment().
get(userId, messageId, attId).execute();
byte[] fileByteArray = Base64.decodeBase64(attachPart.getData());
FileOutputStream fileOutFile =
new FileOutputStream("directory_to_store_attachments" + filename);
fileOutFile.write(fileByteArray);
fileOutFile.close();
}
}
}
I am using GWT and Google App Engine Java for my application. I have a profile screen where
user enters profile information like name, age and address, saves it and gets success or failure message. I developed this initial application using GWT-RPC and it worked fine. I had a new requirement where I have to store image of the user. I am using BlobstoreService to store images. This has created complications in the flow. I had to use FormPanel as it is the only way to do a FileUpload in GWT. The BlobStore service servlet expects a redirect on completion. As a result it cannot now return any status back to my GWT application once the profile is saved. Is there easy to store images using GWT along with other form fields and show a status message back to user once the profile is saved.
i struggled a lot with this problem until yesterday I figured out the solution with much help from Ikai Lan's blog. Basicaly what I did is follow his steps but with a few modifications because doing it exactly how he did it did'nt work for me:
Create a form panel : set encoding multipart, method post.
Make a GWT Remote Service that just has one method:public String getUploadURL() or something like that and in the IMPL write this:
BlobstoreService service = BlobstoreServiceFactory.getBlobstoreService();
return service.createUploadUrl("/XXX/YYY");
In XXX you must put your project path, for example mine is com.fer.pyn.PictureYourNews
In YYY you must put the servlet mapping name for a new servlet that we will have to create: I put XXX = BlobUploader, I created a BlobUploader extends HttpServlet and you have to update the web.xml.
Okey, so this is the weird part that I could'nt figure out, thing is that when we make a RPC call to getUploadURL() in the remote ervice from step 2 that returns a weird addres, like: '/_ah/img/eq871HJL_bYxhWQbTeYYoA' and that is the .fromAction you have to put in your form from step one. You need to update the form's action every time so i suggest the following:
public void initBlobStoreSession()
{
imageService.getBlobStoreUploadURL(new AsyncCallback()
{
#Override
public void onSuccess(String result) {
uploadFormPanel.setAction(result);
System.out.println("Upload Form Panel Action set");
}
#Override
public void onFailure(Throwable caught) {
//oops
}
});
}
So when you submit your fromPanel, IT WILL UPLOAD THE BLOB and you dont have to do anything, the tricky part is how to get the blob:
What you need to do now is create the YYY servlet we where talking about in step 4.
In the post method, this is important:
private BlobstoreService blobService = BlobstoreServiceFactory.getBlobstoreService();
Map<String, BlobKey> blobMap = blobService.getUploadedBlobs(request);
BlobKey blobKey = blobMap.get(UPLOAD_WIDJET_NAME);
UPLOAD_WIDJET_NAME is the .setName for the FileUpload widjet.
What you are doing there is getting a key for yout BLob so you can reference it later.
Our next step is showing the uploaded image back to the GWT layer:
//In the same post method from step 7
ImagesService imagesService = ImagesServiceFactory.getImagesService();
String imageURL = imagesService.getServingUrl(blobKey);
response.sendRedirect("/XXX/YYY?imgURL="+imageURL);
Now in the get method:
String imageUrl = request.getParameter("imgURL");
response.setHeader("Content-Type", "text/html");
response.getWriter().println(imageUrl);
We are done, now you just have to
uploadFormPanel.addSubmitCompleteHandler(new SubmitCompleteHandler() {
#Override
public void onSubmitComplete(SubmitCompleteEvent event) {
uploadFormPanel.reset();
initBlobStoreSession();
String imageUrl = event.getResults();
Image image = new Image();
image.setUrl(imageUrl);
//if you are using jetty, leave this on
//or else it wont work
//Don't use GWT.getModuleBaseURL(), it doesnt
//work well in development mode
imageUrl.replace("http://0.0.0.0:8888/", "");
System.out.println(imageUrl);
final PopupPanel imagePopup = new PopupPanel(true);
imagePopup.setWidget(image);
// Add some effects
imagePopup.setAnimationEnabled(true); // animate opening the image
imagePopup.setGlassEnabled(true); // darken everything under the image
imagePopup.setAutoHideEnabled(true); // close image when the user clicks
imagePopup.center(); // center the image
}
});
check out upload4gwt which address uploading in GWT on AppEngine.
(disclosure: I created upload4gwt; it's not mature yet, however may be useful)
I had the same problem. As a workaround I'm using a redirection to a servlet that print a status message for the client to parse.
I'm passing the websafe string representation of the key to that result servlet.
That's a bit hackey, I'd like someone to come with a better answer, or explain why the blobstore servlet have to redirect.
Yeah, things get more complicated with uploads in GWT.
You can save the form data and image in separate RPCs, and either include a status message in the response to the image upload, or fire off a 3rd RPC when the form returns to get any status or metadata you need.