Image upload from WP7 to Web Api - c#

I have seen few other examples online doing the same, but I am not sure why its not working for me.
I have created a simple windows phone 7 app, which uses PhotoChooserTask.
It sends image to the server using Web Api.
Here is the code in windows phone project:
void selectphoto_Completed(object sender, PhotoResult e)
{
if (e.TaskResult == TaskResult.OK)
{
var image = new Image();
image.Source = new BitmapImage(new Uri(e.OriginalFileName));
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:59551/api/controllername");
request.Method = "POST";
request.ContentType = "multipart/form-data";
//private method to convert bitmap image to byte
byte[] str = BitmapToByte(image);
// Getting the request stream.
request.BeginGetRequestStream
(result =>
{
// Sending the request.
using (var requestStream = request.EndGetRequestStream(result))
{
using (StreamWriter writer = new StreamWriter(requestStream))
{
writer.Write(str);
writer.Flush();
}
}
// Getting the response.
request.BeginGetResponse(responseResult =>
{
var webResponse = request.EndGetResponse(responseResult);
using (var responseStream = webResponse.GetResponseStream())
{
using (var streamReader = new StreamReader(responseStream))
{
string srresult = streamReader.ReadToEnd();
}
}
}, null);
}, null);
}
On the Web API I got the following code for the POST method:
public Task<HttpResponseMessage> Post()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
// Read the form data and return an async task.
var task = Request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(t =>
{
if (t.IsFaulted || t.IsCanceled)
{
Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception);
}
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Image img = Image.FromFile(file.LocalFileName);
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
});
return task;
}
}
However I am not sure why IsMimeMultipartContent is returning false always. Even if I bypass this check, no file is saved in the App_Data folder.
Can anyone please help. Thanks.
EDITED
Based on Darrel's response I have modified the POST method in ApiController. But I still do not get any data. A blank image is created on the server. Here is my code:
public HttpResponseMessage Post()
{
var task = Request.Content.ReadAsStreamAsync();
task.Wait();
Stream requestStream = task.Result;
string root = HttpContext.Current.Server.MapPath("~/App_Data");
root = System.IO.Path.Combine(root, "xyz.jpg");
try
{
FileStream fs = System.IO.File.OpenWrite(root);
requestStream.CopyTo(fs);
fs.Close();
}
catch (Exception)
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError));
}
HttpResponseMessage response = new HttpResponseMessage();
response.StatusCode = HttpStatusCode.Created;
return response;
}

You are not sending a representation that is multipart/form. You are just sending a stream of bytes which is application/octet-stream. Just use Request.Content.ReadAsStreamAsync() on the server and copy the stream to a file.

Related

Get zip file from RestRequest

For REST calls I use RestSharp, I have to call a service that has to return me a zip file which I will then save on File System
public bool DownloadZip(int id)
{
while (true)
{
var request = new RestRequest("download/zip", DataFormat.Json);
request.AddHeader("authorization", _token);
request.AddHeader("ID", _id.ToString());
request.AddQueryParameter("id", id.ToString());
var response = new RestClient(url).Get(request);
response.ContentType = "application/zip";
_logFile.Debug($"downloaded result {id}: {response.IsSuccessful} {response.StatusCode}");
if (response.IsSuccessful && !string.IsNullOrWhiteSpace(response.Content))
{
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(response.Content)))
{
using (var zip = File.OpenWrite(path: #"C:\temp\temp.zip"))
{
zip.CopyTo(stream);
}
}
return true;
}
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
_logFile.Warn($"Download Unauthorized {id}: {response.IsSuccessful} {response.StatusCode} {response.Content}");
_authToken = null;
}
else
{
_logFile.Error($"Download {id}: {response.IsSuccessful} {response.StatusCode} {response.Content}");
throw new Exception("DownloadZip Failed");
}
}
}
The line of code "zip.CopyTo(stream);" returns "The stream does not support reading" as an error.
Is there any setting to set to ensure that it does not return an error?
Testing the call on Postman I noticed that in the response header I have Content-Disposition from this can I go back to the filename?
With RestSharp 107 you can use
var stream = await client.DownloadStreamAsync(request);
It will give you a stream and you can do whatever you want with it.

Get file name from Asp.net Web Api controller

I have a Web Api controller, that gets file. (Server)
[HttpGet]
[Route("api/FileDownloading/download")]
public HttpResponseMessage GetDocuments()
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
var fileName = "QRimage2.jpg";
var filePath = HttpContext.Current.Server.MapPath("");
var fileBytes = File.ReadAllBytes(#"c:\\TMP\\QRimage2.jpg");
MemoryStream fileMemStream = new MemoryStream(fileBytes);
result.Content = new StreamContent(fileMemStream);
var headers = result.Content.Headers;
headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
headers.ContentDisposition.FileName = fileName;
headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
headers.ContentLength = fileMemStream.Length;
return result;
}
And Xamarin Android client, that downloading the file using the controller (http://localhost:6100/api/FileDownloading/download)
public void DownloadFile(string url, string folder)
{
string pathToNewFolder = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, folder);
Directory.CreateDirectory(pathToNewFolder);
try
{
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
string pathToNewFile = Path.Combine(pathToNewFolder, Path.GetFileName(url));
webClient.DownloadFileAsync(new Uri(url), null);
}
catch (Exception ex)
{
if (OnFileDownloaded != null)
OnFileDownloaded.Invoke(this, new DownloadEventArgs(false));
}
}
Everithing works fine, but on my Android device in file explorer i have file with "download" file name instead of "QRimage2.jpg". How can I get actual file name using this controller?
You will need use the web response to read the content disposition. So, we can't use DownloadFileAsync directly.
public async Task<string> DownloadFileAsync(string url, string folder)
{
var request = WebRequest.Create(url);
var response = await request.GetResponseAsync().ConfigureAwait(false);
var fileName = string.Empty;
if (response.Headers["Content-Disposition"] != null)
{
var contentDisposition = new System.Net.Mime.ContentDisposition(response.Headers["Content-Disposition"]);
if (contentDisposition.DispositionType == "attachment")
{
fileName = contentDisposition.FileName;
}
}
if (string.IsNullOrEmpty(fileName))
{
throw new ArgumentException("Cannot be null or empty.", nameof(fileName));
}
var filePath = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, folder, fileName);
using (var contentStream = response.GetResponseStream())
{
using (var fileStream = File.Create(filePath))
{
await contentStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
return filePath;
}
Is this always going to be a jpg? If so I'd change the MediaTypeHeaderValue to image/jpeg - By doing that you are telling the browser the exact type of file, instead of a generic file. I'm thinking this is the issue since you are telling the Android Browser it's just a generic binary file.
Do I need Content-Type: application/octet-stream for file download?

Multipart POST error: Either BinaryRead, Form, Files, or InputStream was accessed before the internal storage was filled

From my MVC app I am trying to do a POST request to my Web API app. After receiving the data inside Web API app, when I try to read it like HttpContext.Current.Request.Form["SenderAddress"]; I get an error saying Either BinaryRead, Form, Files, or InputStream was accessed before the internal storage was filled by the caller of HttpRequest.GetBufferedInputStream. Below is the code for both sending and receiving:
POST request (from MVC):
var form = new MultipartFormDataContent();
var uri = _domain + "/api/email/send";
//adding all properties to the form
if (model.SenderAddress != "" && model.SenderAddress != null)
form.Add(new StringContent(model.SenderAddress), "SenderAddress");
if (model.Recipients.FirstOrDefault() != null)
{
foreach (var recipient in model.Recipients)
form.Add(new StringContent(recipient), "Recipients");
}
if (model.Attachments.FirstOrDefault() != null)
{
foreach (var attachment in model.Attachments)
{
byte[] fileData = null;
using (var binaryReader = new BinaryReader(attachment.InputStream))
{
fileData = binaryReader.ReadBytes(attachment.ContentLength);
}
form.Add(new ByteArrayContent(fileData, 0, fileData.Length), "Attachments", attachment.FileName);
}
}
HttpResponseMessage response = await client.PostAsync(uri, form);
response.EnsureSuccessStatusCode();
string sd = response.Content.ReadAsStringAsync().Result;
Receiving data (at Web API):
var temp3 = HttpContext.Current.Request.Form["SenderAddress"];
Here I get that error. How do I solve it?
i don't understand what this has to do with end of multipart stream but here is the working code:
public async Task<HttpResponseMessage> PostFormData()
{
// Check if the request contains multipart/form-data.
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
try
{
// Read the form data.
await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names.
foreach (MultipartFileData file in provider.FileData)
{
Trace.WriteLine(file.Headers.ContentDisposition.FileName);
Trace.WriteLine("Server file path: " + file.LocalFileName);
}
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
also checked the tutorial code in asp.net site as well:
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/sending-html-form-data-part-2

how to call web api to download document to website directory using webclient

I am struggling with being able to create a file with its data based on the byte array returned from the WebAPI. The following is my code for making the call to the web api
using (var http = new WebClient())
{
string url = string.Format("{0}api/FileUpload/FileServe?FileID=" + fileID, webApiUrl);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
http.DownloadDataCompleted += Http_DownloadDataCompleted;
byte[] json = await http.DownloadDataTaskAsync(url);
}
The api code is
[HttpGet]
[Route("FileServe")]
[Authorize(Roles = "Admin,SuperAdmin,Contractor")]
public async Task<HttpResponseMessage> GetFile(int FileID)
{
using (var repo = new MBHDocRepository())
{
var file = await repo.GetSpecificFile(FileID);
if (file == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var stream = File.Open(file.PathLocator, FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
return response;
}
}
I receive a byte array as a response however am unable to create the corresponding file from that byte array. I have no idea how to convert the byte array into the relevant file type (such as jpg, or pdf based on file type in the web api). any help will be appreciated.
Alright so there are a few ways of solving your problem firstly, on the server side of things you can either simply send the content type and leave it at that or you can also send the complete filename which helps you even further.
I have removed the code that is specific to your stuff with basic test code, please just ignore that stuff and use it in terms of your code.
Some design notes here:
[HttpGet]
[Route("FileServe")]
[Authorize(Roles = "Admin,SuperAdmin,Contractor")]
public async Task<HttpResponseMessage> GetFileAsync(int FileID) //<-- If your method returns Task have it be named with Async in it
{
using (var repo = new MBHDocRepository())
{
var file = await repo.GetSpecificFile(FileID);
if (file == null)
{
throw new HttpResponseException(HttpStatusCode.BadRequest);
}
var stream = File.Open(file.PathLocator, FileMode.Open);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new StreamContent(stream);
response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(file.FileType);
response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment") { FileName=Path.GetFileName(file.PathLocator)};
return response;
}
}
Your client side code has two options here:
static void Main(string[] args)
{
using (var http = new WebClient())
{
string url = string.Format("{0}api/FileUpload/FileServe?FileID={1}",webApiUrl, fileId);
http.Headers[HttpRequestHeader.ContentType] = "application/octet-stream";
http.Headers[HttpRequestHeader.Authorization] = "Bearer " + authCookie.Value;
var response = http.OpenRead(url);
var fs = new FileStream(String.Format(#"C:\Users\Bailey Miller\Downloads\{0}", GetName(http.ResponseHeaders)), FileMode.Create);
response.CopyTo(fs); <-- how to move the stream to the actual file, this is not perfect and there are a lot of better examples
fs.Flush();
fs.Close();
}
}
private static object GetName(WebHeaderCollection responseHeaders)
{
var c_type = responseHeaders.GetValues("Content-Type"); //<-- do a switch on this and return a really weird file name with the correct extension for the mime type.
var cd = responseHeaders.GetValues("Content-Disposition")[0].Replace("\"", ""); <-- this gets the attachment type and filename param, also removes illegal character " from filename if present
return cd.Substring(cd.IndexOf("=")+1); <-- extracts the file name
}

ImageResizer not touching file from webapi 2 controller

I've read this post:
ImageResizer is not resizing images served by WebAPI
and I've read the associated FAQ that talks about WebAPI and ImageResizer here:
http://imageresizing.net/docs/best-practices
I'm still not understanding what I need to do to make files that are served by the route:
config.Routes.MapHttpRoute
("API Vimeo Thumbnail", "rpc/{controller}/{action}/{vimeoId}.jpg",
new
{
});
and handled by this controller:
public class VimeoController : ApiController
{
[HttpGet]
[ActionName("Thumbnail")]
public HttpResponseMessage Thumbnail(string vimeoId = null, int? width = 1280, int? height = 720)
{
string url = String.Format("https://vimeo.com/api/oembed.json?url=https%3A//vimeo.com/{0}&width={1}&height={2}", vimeoId, width,
height);
var endpointRequest = (HttpWebRequest) WebRequest.Create(url);
endpointRequest.Method = "GET";
endpointRequest.Accept = "application/json;odata=verbose";
//var endpointResponse = (HttpWebResponse) endpointRequest.GetResponse();
var result = new HttpResponseMessage(HttpStatusCode.OK);
try
{
using (WebResponse webResponse = endpointRequest.GetResponse())
{
using (Stream webStream = webResponse.GetResponseStream())
{
using (var responseReader = new StreamReader(webStream))
{
string response = responseReader.ReadToEnd();
var reader = new JsonTextReader(new StringReader(response));
while (reader.Read())
{
if (reader.Value != null && reader.Value.Equals("thumbnail_url"))
{
reader.Read();
string downloadUrl = reader.Value.ToString();
var wc = new WebClient();
using (var stream = new MemoryStream(wc.DownloadData(downloadUrl)))
{
result.Content = new ByteArrayContent(stream.ToArray());
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
}
}
}
responseReader.Close();
}
}
}
}
catch (Exception e)
{
Console.Out.WriteLine(e.Message);
Console.ReadLine();
}
return result;
}
}
What confuses me is how ImageResizer picks up files in my /Images directory but not from this handler. Obviously I'm not understanding the pipeline and how to get into it.
ImageResizer does not work this way; it operates prior to the HandleRequest phase in order to take advantage of IIS static file serving. You must subclass BlobProviderBase or implement IVirtualImageProvider if you want to provide custom behavior. If you just need an HTTP request, you might try the RemoteReader plugin (and URL rewriting if you want to change the syntax).
See integrating with a custom data store for more information.

Categories