GWT form upload using BlobstoreService App Engine - google-app-engine

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.

Related

Pass byte array from WPF to WebApi

tl;dr What is the best way to pass binary data (up to 1MBish) from a WPF application to a WebAPI service method?
I'm currently trying to pass binary data from a WPF application to a WebAPI web service, with variable results. Small files (< 100k) generally work fine, but any larger and the odds of success reduce.
A standard OpenFileDialog, and then File.ReadAllBytes pass the byte[] parameter into the client method in WPF. This always succeeds, and I then post the data to WebAPI via a PostAsync call and a ByteArrayContent parameter.
Is this the correct way to do this? I started off with a PostJSONAsync call, and passed the byte[] into that, but thought the ByteArrayContent seemed more appropriate, but neither work reliably.
Client Method in WPF
public static async Task<bool> UploadFirmwareMCU(int productTestId, byte[] mcuFirmware)
{
string url = string.Format("productTest/{0}/mcuFirmware", productTestId);
ByteArrayContent bytesContent = new ByteArrayContent(mcuFirmware);
HttpResponseMessage response = await GetClient().PostAsync(url, bytesContent);
....
}
WebAPI Method
[HttpPost]
[Route("api/productTest/{productTestId}/mcuFirmware")]
public async Task<bool> UploadMcuFirmware(int productTestId)
{
bool result = false;
try
{
Byte[] mcuFirmwareBytes = await Request.Content.ReadAsByteArrayAsync();
....
}
Web Config Settings
AFAIK these limits in web.config should be sufficient to allow 1MB files through to the service?
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
<httpRuntime targetFramework="4.5" maxRequestLength="2097152"/>
I receive errors in WebAPI when calling ReadAsByteArrayAsync(). These vary, possibly due to the app pool in IIS Express having crashed / getting into a bad state, but they include the following (None of which have lead to any promising leads via google):
Specified argument was out of the range of valid values. Parameter name: offset
at System.Web.HttpInputStream.Seek(Int64 offset, SeekOrigin origin)\r\n
at System.Web.HttpInputStream.set_Position(Int64 value)\r\n at System.Web.Http.WebHost.SeekableBufferedRequestStream.SwapToSeekableStream()\r\n at System.Web.Http.WebHost.Seek
OR
Message = "An error occurred while communicating with the remote host. The error code is 0x800703E5."
InnerException = {"Overlapped I/O operation is in progress. (Exception from HRESULT: 0x800703E5)"}
at System.Web.Hosting.IIS7WorkerRequest.RaiseCommunicationError(Int32 result, Boolean throwOnDisconnect)\r\n
at System.Web.Hosting.IIS7WorkerRequest.ReadEntityCoreSync(Byte[] buffer, Int32 offset, Int32 size)\r\n
at System.Web.Hosting.IIS7WorkerRequ...
Initially I thought this was most likely down to IIS Express limitations (running on Windows 7 on my dev pc) but we've had the same issues on a staging server running Server 2012.
Any advice on how I might get this working would be great, or even just a basic example of uploading files to WebAPI from WPF would be great, as most of the code I've found out there relates to uploading files from multipart forms web pages.
Many thanks in advance for any help.
tl;dr It was a separate part of our code in the WebApi service that was causing it to go wrong, duh!
Ah, well, this is embarrassing.
It turns out our problem was down to a Request Logger class we'd registered in WebApiConfig.Register(HttpConfiguration config), and that I'd forgotten about.
It was reading the request content via async as StringContent, and then attempting to log it to the database in an ncarchar(max) field. This itself is probably OK, but I'm guessing all the weird problems started occurring when the LoggingHandler as well as the main WebApi controller, were both trying to access the Request content via async?
Removing the LoggingHandler fixed the problem immediately, and we're now able to upload files of up to 100MB without any problems. To fix it more permanently, I guess I rewrite of the LoggingHandler is required to set a limit on the maximum content size it tries to log / to ignore certain content types.
It's doubtful, but I hope this may be of use for someone one day!
public class LoggingHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
LogRequest(request);
return base.SendAsync(request, cancellationToken).ContinueWith(task =>
{
var response = task.Result;
// ToDo: Decide if/when we need to log responses
// LogResponse(response);
return response;
}, cancellationToken);
}
private void LogRequest(HttpRequestMessage request)
{
(request.Content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
{
try
{
var callerId = CallerId(request);
var callerName = CallerName(request);
// Log request
LogEntry logEntry = new LogEntry
{
TimeStamp = DateTime.Now,
HttpVerb = request.Method.ToString(),
Uri = request.RequestUri.ToString(),
CorrelationId = request.GetCorrelationId(),
CallerId = callerId,
CallerName = callerName,
Controller = ControllerName(request),
Header = request.Headers.ToString(),
Body = x.Result
};
...........

Same functionality as HTTPClient in Codename one

I was wondering how I can achieve something like an HTTPClient.
I tried WebBrowser class but it seems that the execution continues even though the URL specified has not yet loaded.
public void testWebBrowser(){
final WebBrowser b = new WebBrowser(){
#Override
public void onLoad(String url) {
BrowserComponent c = (BrowserComponent)this.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
// I want this Javascript context here
}
};
// just a test URL
b.setURL("http://youtube.com");
// Suppose to get the Javascript context here though it executes without waiting for the whole page to load
}
How can I get the JS Context from within a WebBrowser context? Like a synchronous execution
WebBrowser browser = new WebBrowser();
browser.setURL("someURL");
// wait execution till the whole page in "someURL" loads till it executes the next line
BrowserComponent c = (BrowserComponent)browser.getInternal();
JavascriptContext ctx = new JavascriptContext(c);
If I understand correctly you are trying to create a scraping solution?
That's probably not the ideal approach since this will actually create a web browser which you then need to automate with JavaScript. I would suggest you create a webservice that encapsulates the HttpClient functionality and drive it with ConnectionRequest. This way when the web site changes you can just fix your server in a way seamless to your installed base.

What is the best way to store user specific files in Wicket?

I am creating a file that is user specific. This file is basically a results csv that is created with the option for the user to download or not. When the user leaves the page, or ends their session I want to be able to delete this file. What is the best way to handle this?
Currently I am using the File class for Java.
Thanks!
You don't have to write a file in the first place. Create the content on the fly and stream it back to the client. Wicket has a few classes in the package org.apache.wicket.request.resource to help with that.
As a starting point, look at Wicket 6 resource management and Wicket 1.5 Mounting resources
You basically mount a resource in the WicketApplication.init():
mountResource("somePath/${param1}/${param2}", new SomeResourceReference());
Than the SomeResourceReference:
public class SomeResourceReference extends ResourceReference {
#Override
public IResource getResource() {
return new SomeResource();
}
}
And finally in SomeResource:
public class SomeResource extends AbstractResource {
#Override
public AbstractResource.ResourceResponse
newResourceResponse(Attributes attributes) {
// get the parameters
PageParameters parameters = attributes.getParameters();
final String param1 = parameters.get("param1").toStringObject();
AbstractResource.ResourceResponse response
= new AbstractResource.ResourceResponse();
response.setContentType("application/CSV");
response.setCacheDuration(Duration.NONE);
response.setCacheScope(WebResponse.CacheScope.PRIVATE);
response.setContentDisposition(ContentDisposition.INLINE);
response.setWriteCallback(new AbstractResource.WriteCallback() {
#Override
public void writeData(final Attributes attributes) throws IOException {
// create your data here
attributes.getResponse().write(dataAsString);
}
});
return response;
}
}
Wicket doesn't control destroying the session. It is the concern of the servlet container you are using.
If you want to create a file in Wicket and delete the file when the session is destroyed or user want logout, it has two parts:
User logout (in Wikcet)
Store the file path or the file reference in the WebSession (Wicket)
Override the method invalidate() of your WebSession or AutheticatedWebSession, see http://ci.apache.org/projects/wicket/apidocs/6.x/org/apache/wicket/protocol/http/WebSession.html#invalidate%28%29
Session destroyed
Store the file path or the file reference into the container session and write your listener and add it to the your servlet context (e.g. tomcat using web.xml file).
See http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionListener.html

How to pass a search term to Bing from silverlight or google

Has anyone had any luck passing from Silverlight to Bing or Google a url parameter based string that will open search results in a browser. I have been using the Bing API and been able to search and return results within the client and I have also done this using JSON and passing values to the Google API. However if you want to just send a query string to either service and have the results returned via a browser result list I have not found this to be possible. What I am trying to see if it is possible to pass a term "gocart" to Google or Bing as a url (http:www.bing.com/query?gocart) and it return in a new browser window results of the search term. From my initial research both search engines appear to prevent unauthorized queries via URL, I was curious if anyone found it different.
thanks
I have made a hyperlink class:
private class HyperlinkButtonWrapper : HyperlinkButton
{
public void OpenURL(string navigateUri)
{
OpenURL(new Uri(navigateUri, UriKind.Absolute));
}
public void OpenURL(Uri navigateUri)
{
base.NavigateUri = navigateUri;
base.TargetName = "_blank";
base.OnClick();
}
}
Then use it like this:
private void ButtonSearch_Click(object sender, System.Windows.RoutedEventArgs e)
{
var hyperlinkwrapper = new HyperlinkButtonWrapper();
hyperlinkwrapper.OpenURL(#"http://www.google.com/#q=gocart");
}
I found that on this blog
couldn't test it right now but try:
HtmlPage.Window.Navigate(new Uri("http://www.bing.com/query?gocart", UriKind.Absolute), "_blank");
This should open a new browser window or tab with the url supplied... wouldn't expect any differences between Google and Bing in this regard.
The MSDN reference link is http://msdn.microsoft.com/en-us/library/cc190508%28v=VS.95%29.aspx
If you use google you can get html rendered results using Yahia's suggestion and open a link formatted such as this http://www.google.com/#q=gocart.
Out of the few api options I tried from the search api documentation I could only change the language without entering my API key.
(http://www.google.com/#q=gocart&hl=fr for French) Page result size etc failed but if you're looking for something quick and dirty with not much control the above will work.

Windows Phone 7 App Quits when I attempt to deserialize JSON

I'm developing my first windows phone 7 app, and I've hit a snag. basically it's just reading a json string of events and binding that to a list (using the list app starting point)
public void Load()
{
// form the URI
UriBuilder uri = new UriBuilder("http://mysite.com/events.json");
WebClient proxy = new WebClient();
proxy.OpenReadCompleted += new OpenReadCompletedEventHandler(OnReadCompleted);
proxy.OpenReadAsync(uri.Uri);
}
void OnReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
var serializer = new DataContractJsonSerializer(typeof(EventList));
var events = (EventList)serializer.ReadObject(e.Result);
foreach (var ev in events)
{
Items.Add(ev);
}
}
}
public ObservableCollection<EventDetails> Items { get; private set; }
EventDetails is my class that wraps the json string. this class has to be correct because it is an exact copy of the class used by that website internally from which the json is generated...
I get the json string correctly from the webclient call (I read the memorystream and the json is indeed there) but as soon as I attempt to deserialize the string, the application exits and the debugger stops.
I get no error message or any indication that anything happen, it just stops. This happens if I type the deserialize method into the watch window as well...
I have already tried using JSON.net in fact I thought maybe it was a problem with JSON.net so I converted it to use the native deserializer in the .net framework but the error is the same either way.
why would the application just quit? shouldn't it give me SOME kind of error message?
what could I be doing wrong?
many thanks!
Firstly, the fact that you have some string there that looks like JSON does not mean that you have a valid JSON. Try converting a simple one.
If your JSON is valid, it might be that your JSON implementation does not know how to convert a list to EventList. Give it a try with ArrayList instead and let me know.
The application closes because an unhandled exception happens. If check the App.xaml.cs file you will find the code that closes your app. What you need to do is try catch your deserialization process and handle it locally. So most likely you have some JSON the DataContractJsonSerializer does not like. I have been having issue with it deserializing WCF JSON and have had to go other routes.
You may want to check to ensure your JSON is valid, just because your website likes it does not mean it is actually valid, the code on your site may be helping to correct the issue. Drop a copy of your JSON object (the string) in http://jsonlint.com/ to see if it is valid or not. Crokford (the guy who created JSON) wrote this site to validate JSON, so I would rely on it more than your site ;) This little site has really helped me out of some issues over the past year.
I ran into this same kind of problem when trying to migrate some existing WM code to run on WP7. I believe that the WP7 app crashes whenever it loads an assembly (or class?) that references something that's not available in WP7. In my case, I think it was Assembly.Load or something in the System.IO namespace, related to file access via paths.
While your case might be something completely different, the symptoms were exactly the same.
The only thing I can recommend is to go through the JSON library and see if it's referencing base classes that are not allowed in WP7. Note that it doesn't even have to hit the line of code that's causing the issue - it'll crash as soon as it tries to hit the class that contains the bad reference.
If you can step into the JSON library, you can get a better idea of which class is causing the problem, because as soon as the code references it, the whole app will crash and the debugger will stop.

Resources