I am using the Background Transfer to upload Photographs to my Web Service. As the Photograph uploads can consume significant time and memory, I thought it might be a nice idea to use the background transfer request to accomplish this. After the photo is uploaded, I want to obtain the Id of the uploaded photo and then use it for post-processing. However, it turns out I can't do that in a background transfer request.
Per my understanding, Background Transfer works using the following logic ONLY:
You have to obtain the file you want to upload and then save/copy it to your app's Isolated Storage under the folder: shared/transfers. This is extremely important. Apparently, using file in a different location didn't work for me. Maybe it isn't the shared/transfers as much as it is a 'relative' path. But I would stick to the same conventions.
After you have saved the file in that location, your background request can be created based on that. It doesn't look like you can pass POST CONTENT other than the file contents, so any other parameters like file name, mime type etc. will need to be passed as QUERY String parameters only. I can understand this, but it would've been nice if I could pass both as POST Content. I don't think HTTP has a limitation on how this works.
Here is some code for creating a request using Hammock:
string url = App.ZineServiceAuthority + "articles/save-blob?ContainerName={0}&MimeType={1}&ZineId={2}&Notes={3}&IsPrivate={4}&FileName={5}";
url = String.Format(url, userId, "image/jpg", ZineId, txtStatus.Text, true, UploadFileName);
var btr = new BackgroundTransferRequest(new Uri(url, UriKind.Absolute));
btr.TransferPreferences = TransferPreferences.AllowCellularAndBattery;
btr.Method = "POST";
btr.Headers.Add("token", IsolatedStorageHelper.GetTravzineToken());
btr.UploadLocation = new Uri(#"/shared\transfers/" + UploadFileName, UriKind.Relative);
btr.TransferStatusChanged += new EventHandler<BackgroundTransferEventArgs>(btr_TransferStatusChanged);
btr.TransferProgressChanged += new EventHandler<BackgroundTransferEventArgs>(btr_TransferProgressChanged);
BackgroundTransferService.Add(btr);
In my case, I am literally passing all the necessary parameters using the query string. On a successful save, my Web Service returns back the Id of the Photo I just uploaded. However:
There is NO way (or at least I know of) to obtain and evaluate the RESPONSE. The Background Transfer Request Event handlers do not expose a RESPONSE.
Here are my event handlers:
void btr_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
bool isUploading = e.Request.TotalBytesToSend > 0 ? true : false;
lblStatus.Text = isUploading ? "Uploading" + e.Request.BytesSent.ToString() + " sent" : "Done";
}
void btr_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
if (e.Request.TransferStatus == TransferStatus.Completed)
{
using (IsolatedStorageFile iso =
IsolatedStorageFile.GetUserStoreForApplication())
{
if (iso.FileExists(e.Request.UploadLocation.OriginalString))
iso.DeleteFile(e.Request.UploadLocation.OriginalString);
}
BackgroundTransferService.Remove(e.Request);
if (null != e.Request.TransferError)
{
MessageBox.Show(e.Request.TransferError.Message);
}
else
{
lblStatus.Text = "Done baby done";
}
}
}
So now my question is, how does anyone do any sort of POST Processing in such scenarios?
Can anyone please tell me the line of thought behind designing such an inflexible class?
Any thoughts on how I could get around this issue would be appreciated.
Also, does anyone have any working examples of a homegrown BackgroundTransfer?
Haven't tried it but why not set a download location like this:
btr.DownloadLocation = "myDownloadFile.html";
btr.UploadLocation = "myUploadFile.jpg";
...
If the request is completed read the file "myDownloadFile.html" where your response has been stored and delete it afterwards.
Related
I am trying to make a XBAP application communicating with a webservice with login.
But I want the user to skip the login step if they already logged in the last seven days.
I got it to work using html/aspx.
But it fails continuously with XBAP.
While debugging, the application is given full trust.
This is the code I have so far to write the cookie:
protected static void WriteToCookie(
string pName,
Dictionary<string, string> pData,
int pExiresInDays)
{
// Set the cookie value.
string data = "";
foreach (string key in pData.Keys)
{
data += String.Format("{0}={1};", key, pData[key]);
}
string expires = "expires=" + DateTime.Now.AddDays(pExiresInDays).ToUniversalTime().ToString("r");
data += expires;
try
{
Application.SetCookie(new Uri(pName), data);
}
catch (Exception ex)
{
}
}
And this is what I have to read the cookie:
protected static Dictionary<string, string> ReadFromCookie(
string pName)
{
Dictionary<string, string> data = new Dictionary<string, string>();
try
{
string myCookie = Application.GetCookie(new Uri(pName));
// Returns the cookie information.
if (String.IsNullOrEmpty(myCookie) == false)
{
string[] splitted = myCookie.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
string[] sub;
foreach(string split in splitted)
{
sub = split.Split(new char[] { ':' }, StringSplitOptions.RemoveEmptyEntries);
if (sub[0] == "expires")
{
continue;
}
data.Add(sub[0], sub[1]);
}
}
}
catch(Exception ex)
{
}
return data;
}
The pName is set with:
string uri = "http://MyWebSiteName.com";
When the user authenticate the first time, I call the WriteToCookie function and set it with 7 days to expire.
It looks like everything is fine as I get no exception of error messages. (I have a break point in the catch)
After that, I close the session and start it again.
The first thing I do is a ReadFromCookie.
Then I get an exception with the following message: No more data is available
So my application is sending the user automatically back to the login screen.
I also tried to do a ReadFromCookie right after the WriteToCookie in the same session, and I get the same error.
Application.SetCookie(new Uri("http://MyWebSiteName.com/WpfBrowserApplication1.xbap"), "Hellllo");
string myCookie2 = Application.GetCookie(new Uri("http://MyWebSiteName.com/WpfBrowserApplication1.xbap"));
It seems to me that the cookie is not even written in the first place.
So I am guessing I am doing something wrong.
Maybe the uri I am using is wrong. Is there a specific format needed for it?
Just like you need a very specific format for the expire date.
I have been searching quite a lot of internet for a good sample/tutorial about using cookies with XBAP, and I could not find anything really well documented or tested.
A lot of people say that it works, but no real sample to try.
A lot of people also handle the authentication in html, then go to the XBAP after successfully reading/writing the cookies.
I would prefer a full XBAP solution if possible.
To answer some questions before they are asked, here are the project settings:
Debug:
Command line arguments: -debug -debugSecurityZoneURL http://MyWebSiteName.com "C:\Work\MyWebSiteName\MyWebSiteNameXBAP\bin\Debug\MyWebSiteNameXBAP.xbap"
Security:
Enable ClickOnce security settings (Checked)
This is a full trust application (selected)
I also created a certificate, and added it the 3 stores like explained in "publisher cannot be verified" message displayed
So I do not have the warning popup anymore. I just wanted to make sure that it was not a permission issue.
Finally found the answer to this problem.
Thanks for this CodeProject I was finally able to write/read cookies from the XBAP code.
As I had guessed, the URI needs to be very specific and you cannot pass everything you want in it.
What did the trick was using: BrowserInteropHelper.Source
In the end the read/write code looks like:
Application.SetCookie(BrowserInteropHelper.Source, data);
string myCookie = Application.GetCookie(BrowserInteropHelper.Source);
It looks like you cannot use ';' to separate your own data.
If you do so, you will only get the first entry in your data.
Use a different separator (ex: ':') and then you can get everything back
The data look like this:
n=something:k=somethingElse;expires=Tue, 12 May 2015 14:18:56 GMT ;
The only thing I do not get back from Application.GetCookie is the expire date.
Not sure if it is normal or not. Maybe it is flushed out automatically for some reason. If someone knows why, I would appreciate a comment to enlighten me.
At least now I can read/write data to the cookie in XBAP. Yeah!
I'm developing a WebAPI service in which you can upload a file. The Action looks something like this:
[HttpPost]
public async Task<IHttpActionResult> PostAsync(byte[] content)
{
var now = DateTime.UtcNow;
}
The client which are using the WebAPI also provides a timestamp as header which is used together with some HMAC-stuff to authenticate. One part of the auth check is to validate the timestamp. We parse the timestamp and checks if it is +/- 5 minutes from now. If not then the auth fails.
It works great for all our API calls except this upload API (in some cases). The problem is that sometimes a user uploads a large file over a slow connection and therefore it takes more than 5 minutes to upload the file and the point in time where we check is AFTER the whole file has been uploaded.
Therefore:
Can we somehow do the HMAC check BEFORE the whole file is uploaded? (the file itself (HTTP Content) is not used in the HMAC check). Today we are using an ActionFilter.
Can I get the "time of request" (first byte arrived or whatever) in my Action code?
Thanks!
So, after some investigation I came up with a much better solution:
Use a HTTP Module to do the actual HMAC authentication.
After reading this blog post (http://blogs.msdn.com/b/tmarq/archive/2007/08/30/iis-7-0-asp-net-pipelines-modules-handlers-and-preconditions.aspx) I got a much better understanding of how IIS Works.
I decided to use a HTTP Module which is invoked before the MVC Action.
The code ended up like this:
public class HmacModule : IHttpModule
{
public void Init(HttpApplication context)
{
EventHandlerTaskAsyncHelper taskAsyncHelper = new EventHandlerTaskAsyncHelper(Authenticate);
context.AddOnBeginRequestAsync(taskAsyncHelper.BeginEventHandler, taskAsyncHelper.EndEventHandler);
}
private async Task Authenticate(object sender, EventArgs e)
{
var context = ((HttpApplication)sender).Context;
var request = context.Request;
var authResponse = await CheckAuthentication(request);
if (!authResponse.HasAccess)
{
context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
context.Response.StatusDescription = authResponse.ErrorMessage;
if (authResponse.Details != null)
context.Response.Write(authResponse.Details);
context.Response.End();
}
}
}
I hope this helps others in the same situation...
I am getting a list of objects in Windows Phone, and show them in a listbox with databinding.
some image urls are not valid, so after every object is added in the list, i run the following code to check and replace, if not valid
private void CheckLinkUrl(Person p)
{
Uri filePath = new Uri(p.img_url);
string correct = p.img_url;
HttpWebRequest fileRequest = HttpWebRequest.CreateHttp(filePath);
fileRequest.Method = "HEAD";
fileRequest.BeginGetResponse(result =>
{
HttpWebRequest resultInfo = (HttpWebRequest)result.AsyncState;
HttpWebResponse response;
try
{
response = (HttpWebResponse)resultInfo.EndGetResponse(result);
}
catch (Exception e)
{
p.img_url = "http://somethingelse.com/image.jpg";
}
}, fileRequest);
}
the problem is that it is very slow, it takes sometimes 2 minutes+ to load every image (although the UI remains responsive, and everything else is displayed immediately in the listbox, apart from the images)
am I doing something wrong? can i get it to run faster?
EDIT:
I tried using the imagefailed event and replace the link, no improvement at the speed of loading the pics
What I have done to avoid this problem in my application is, I have loaded the items with a default Image, The image source is binded to a property in my result item of type ImageSource. By default it returns the default image. After processing or download completion the imagesource value changes to the new Image triggering the NotifyPropertyChanged event and hence it is automatically reflected on the UI. I hope it helps you.
Actually my question is short.
How can I get a HttpPostedFile from a ASP.NET Web API POST or PUT?
I did see that I can get various information from the Request like Request.Header, Request.Content, Request.Properties. Where in there can I find the file I passed and how can I create a HttpPostedFile from it?
Thanks in advance!
Check out the great article from Henrik Nielsen to post multi-part content (i.e posting a form with file)
UPDATE: Add simple code for a controller to receive a file without multipart content
If you only need your controller to receive a file (i.e. no multipart content), you could do something like the above. The request only contains the file binary and the filename is passed within the URL.
public Task<HttpResponseMessage> Post([FromUri]string filename)
{
Guid uploadedFile = Guid.NewGuid();
Task<HttpResponseMessage> task = Request.Content.ReadAsStreamAsync().ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
try
{
using (Stream stream = t.Result)
{
//TODO: Write the stream to file system / db as you need
}
}
catch (Exception e)
{
Object o = e;
return Request.CreateResponse(HttpStatusCode.InternalServerError, e.GetBaseException().Message);
}
return Request.CreateResponse(HttpStatusCode.Created, uploadedFile.ToString());
});
return task;
}
Your short question does not have a short answer I am afraid.
ASP.NET Web API exposes you to the wonders of HTTP while ASP.NET MVC abstracted some of it - in this case for HttpPostedFile.
So a bit of background:
HTTP posts where a file is involved usually has multipart form data content. This means that you are mixing different kind of content-type: your normal form data will be sent using formurlencoded while the files will be sent application/octent-stream.
So in Web API, all you have to do is to say
var contents = message.Content.ReadAsMultipartAsync(); // message is HttpRequestMessage
One of the contents will contain your file.
My script fetches xml via httpConnection and saves to persistent store. No problems there.
Then I loop through the saved data to compose a list of image url's to fetch via queue.
Each of these requests calls the httpConnection thread as so
...
public synchronized void run()
{
HttpConnection connection = (HttpConnection)Connector.open("http://www.somedomain.com/image1.jpg");
connection.setRequestMethod("GET");
String contentType = connection.getHeaderField("Content-type");
InputStream responseData = connection.openInputStream();
connection.close();
outputFinal(responseData, contentType);
}
public synchronized void outputFinal(InputStream result, String contentType) throws SAXException, ParserConfigurationException, IOException
{
if(contentType.startsWith("text/"))
{
// bunch of xml save code that works fine
}
else if(contentType.equals("image/png") || contentType.equals("image/jpeg") || contentType.equals("image/gif"))
{
// how to save images here?
}
else
{
//default
}
}
What I can't find any good documentation on is how one would take the response data and save it to an image stored on the device.
Maybe I just overlooked something very obvious. Any help is very appreciated.
Thanks
I tried following this advise and found the same thing I always find when looking up BB specific issues: nothing.
The problem is that every example or post assumes you know everything about the platform.
Here's a simple question: What line of code writes the read output stream to the blackberry device? What path? How do I retrieve it later?
I have this code, which I do not know if it does anything because I don't know where it is supposedly writing to or if that's even what it is doing at all:
** filename is determined on a loop based on the url called.
FileOutputStream fos = null;
try
{
fos = new FileOutputStream( File.FILESYSTEM_PATRIOT, filename );
byte [] buffer = new byte [262144];
int byteRead;
while ((byteRead = result.read (buffer ))!=- 1)
{
fos.write (buffer, 0, byteRead);
}
fos.flush();
fos.close();
}
catch(IOException ieo)
{
}
finally
{
if(fos != null)
{
fos.close();
}
}
The idea is that I have some 600 images pulled from a server. I need to loop the xml and save each image to the device so that when an entity is called, I can pull the associated image - entity_id.png - from the internal storage.
The documentation from RIM does not specify this, nor does it make it easy to begin figuring it out.
This issue does not seem to be addressed on this forum, or others I have searched.
Thanks
You'll need to use the Java FileOutputStream to do the writing. You'll also want to close the connection after reading the data from the InputStream (move outputFinal above your call to close). You can find all kinds of examples regarding FileOutputStream easily.
See here for more. Note that in order to use the FileOutputStream your application must be signed.