Sending multiple responses from a Web Service - c#

I want to create a Web Service that receives images in the form of byte[] and Saves them in the FileSystem.
I want to keep sending the response as I'm saving the files on the FileSystem so that user can show the progress on the mobile device.
Currently I have built a Web Service that can receive only single image.
Here's my code to save single image-
[WebMethod]
public string upload(byte[] postedFile, string folderName, string fileName)
{
try
{
string to_post_in = Path.Combine(HttpContext.Current.Server.MapPath("~/"), folderName);
File.WriteAllBytes(Path.Combine(to_post_in, fileName), postedFile);
SortedList slResult = new SortedList();
slResult.Add("0", "Success");
return JsonConvert.SerializeObject(slResult);
}
catch
{
SortedList slResult = new SortedList();
slResult.Add("1", "Error");
return JsonConvert.SerializeObject(slResult);
}
}

This might not be possible!
As best I know, once you send a response back, code does not continue any further from that line.
You may do the following-
Use same code.
Send one by one pic from the iOS device.
That way you'll be able to tell what file was stored successfully and what not.
Hope it helped!

You can't do this in one single function in Web Services
Why not trying to create the following?
Function upload to save the list of files like this
public string upload(List<byte[]> postedFile, string folderName, List<string> fileNameList)
{
//code to save here
}
Function checkUploaded that takes a list of images name as parameter and checks how many images have already been saved-
public string checkUploaded(string folderName, List<string> fileNameList)
{
//add code to check how many images of the list have already been saved
//and return a percent
}

Related

Not getting the Uploaded filepath in asp.net

I want to save file to a specific location with some folder creation based on my requirement. So I wrote the below code.
public string CreateFilePath(string addedFolderName)
{
string folderPath = ConfigurationManager.AppSettings["DocDirectory"].ToString();
string FileUplPath = folderPath + "\\" + addedFolderName + "\\";
if (!Directory.Exists(FileUplPath))
{
Directory.CreateDirectory(FileUplPath);
}
flUploadDocs.SaveAs(FileUplPath + Path.GetFileName(flUploadDocs.FileName));
return folderPath;
}
But I am unable to get the filepath here. I am getting it as null
getting null at
Path.GetFileName(flUploadDocs.FileName)
<asp:FileUpload ID="flUploadDocs" runat="server" />
Please suggest what is wrong here.
Path.GetFileName() returns the file name and extension of the specified path string
if im correct this only fills in the file name and not the directory + name.
Path.GetFileName(flUploadDocs.FileName)
possible solution
Path.GetFileName(FileUplPath+flUploadDocs.FileName)
eventough im confused why you try to retrieve the path again after just having saved it?
The issue is that the webservice does not have the fileupload data. Here is the full code from our extended conversation:
[WebMethod]
public static string InsertUpdateMWSiteData(MWInsertUpdateFields MWInsertUpdateFields)
{
string strInsertUpdateMWInfo = "";
try
{
Dashboard dshb = new Dashboard();
dshb.CreateFilePath(MWInsertUpdateFields.SapID + "_" + MWInsertUpdateFields.CandidateID);
strInsertUpdateMWInfo = CommonDB.InsertUpdateMWSiteInfo(MWInsertUpdateFields);
}
catch (Exception)
{
throw;
}
return strInsertUpdateMWInfo;
}
public string CreateFilePath(string addedFolderName)
{
string folderPath = ConfigurationManager.AppSettings["DocDirectory"].ToString();
string FileUplPath = folderPath + "\\" + addedFolderName + "\\";
if (!Directory.Exists(FileUplPath))
{
Directory.CreateDirectory(FileUplPath);
}
if (flUploadDoc.HasFile == true)
{
string strFilename = Path.GetFileName(flUploadDoc.FileName);
flUploadDoc.SaveAs(FileUplPath + Path.GetFileName(flUploadDoc.PostedFile.FileName));
}
return folderPath;
}
The problem is that after uploading a file, a request is sent to a webmethod which is being hosted in another instance of the program. This Webmethod checks its own instance for the fileupload control and data, and doesn't find it because it is in a different instance. This is why your fileupload control is returning null even on a sanity check of .HasFile().
One solution is to pass the data to the Webservice. You could for example pass the data to your webmethod as a byte[], and then on the webservice side reconvert it back into its original file type. After completing this process, save the file to your local filesystem. To do this you may need to pass the extension type and file name.
You may also want to add some validation to limit the file types accepted to only the most common file types like images, .doc, .excel, and whatever you have the library to support the conversion of.
If you want to save files directly to your filesystem using the upload control, you can do so but you will have to exclude the webservice step.
Please also see the discussion in chat for details.

The process cannot access the file 'C:\file.txt' because it is being used by another process

I am trying to log each method on my program, I have the application deployed on IIS Server and the user just called me and said the email functionality is not working so I need to basically run the application but log each step into a txt file.
I am declaring the below as a global value:
StreamWriter writer = new StreamWriter("C:\\file.txt");
Then I use it like below in my code:
Method 1
{
if (file1.HasFile)
{
writer.WriteLine("Has File");
}
}
Method 2
private Boolean InsertUpdateData(SqlCommand cmd)
{
writer.WriteLine("Insert Started" + DateTime.Now.ToString());
}
So in my case method one runs fine and it writes Has File but when it goes into the second method I get the file is already open which is correct how can I work around this?
Thanks
Global Value - declared at top
namespace WorkOrderManagement
{
public partial class CreateWorkOrder : System.Web.UI.Page
{
bool successfull;
string path;
string name;
string content;
string datas;
string ext;
bool affectedrows;
string seasonalsupervisor;
private string sLogFormat;
private string sErrorTime;
StreamWriter writer = new StreamWriter("C:\\file.txt");
I really suggest you to discard the idea to have a global variable to represent a stream and then try to use it in different methods. This is simple in a desktop application, but a lot more complex in an ASP.NET application.
There are simple alternatives that could atomically write your log text and leave the file unlocked.
For example you could have a method like this
public static class Log
{
public static string _file = "log.txt";
public static object _locked = new object();
public static void AppendToLog(string text)
{
lock(_locked)
{
string path = Server.MapPath("~/APP_DATA");
File.AppendAllText(Path.Combine(path, _file), text + Environment.NewLine);
}
}
}
Now you can call the log write with
Log.AppendToLog("My message");
I want to underline two important things here. First I don't write in the root drive of the server. This is a bad practice and always a source of problems when you deploy your ASP.NET application in a server where you have not permissions to use anything outside your site root. Thus the ASP.NET system defines a particular folder called APP_DATA under your site root where your application should have read/write permissions.
Second point to notice is the use of the lock keyword. This is necessary in an environment like ASP.NET where two users could reach a point of the code where you need to write to the common log file. As MSDN explains it
The lock keyword ensures that one thread does not enter a critical
section of code while another thread is in the critical section. If
another thread tries to enter a locked code, it will wait, block,
until the object is released.
you can also do this to close the file stream
using (StreamWriter writer = new StreamWriter("C:\\file.txt"))
{
//your code here
}
//this automatically closes the stream, and it is more recommended.
Close the stream after writing the file
Method 1
{
if (file1.HasFile)
{
writer.WriteLine("Has File");
writer.Close();
}
}

Uploading file with post from ASP.NET web api throws error when published to Azure but not localhost

I have this web api where I'm posting images to a folder in my web api and it works fine when doing it locally but when publishing the web api online it doesn't work and throws the following error message "Error writing MIME multipart body part to output stream". I've seen a few people with similar questions but i havent been able to solve it therefore im putting the code out there and hopefully someone could notice what may be causing this! here is the code:
public async Task<HttpResponseMessage> Post()
{
// Check whether the POST operation is MultiPart?
if (!Request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
// Prepare CustomMultipartFormDataStreamProvider in which our multipart form
// data will be loaded.
string fileSaveLocation = HttpContext.Current.Server.MapPath("~/App_Data");
CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation);
List<string> files = new List<string>();
try
{
// Read all contents of multipart message into CustomMultipartFormDataStreamProvider.
//Here the code goes down to public class CustomMultipartFormDataStreamProvider and runs that code and then jump back here. When it gets back here is when the Error is Thrown
await Request.Content.ReadAsMultipartAsync(provider);
foreach (MultipartFileData file in provider.FileData)
{
files.Add(Path.GetFileName(file.LocalFileName));
}
// Send OK Response along with saved file names to the client.
return Request.CreateResponse(HttpStatusCode.OK, files);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}
public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public CustomMultipartFormDataStreamProvider(string path) : base(path) { }
public override string GetLocalFileName(HttpContentHeaders headers)
{
return headers.ContentDisposition.FileName.Replace("\"", string.Empty);
}
}
The code is from http://www.intstrings.com/ramivemula/articles/file-upload-using-multipartformdatastreamprovider-in-asp-net-webapi/ where you can read more about the code. Any help or input highly appreciated, thanks!
I am not sure you can store data in the App_Data.
It will be a thousand times better if you store all your files in the Blob storage. If you need any help I will assist.
Follow this article and you will get it running.
https://azure.microsoft.com/en-us/documentation/articles/storage-dotnet-how-to-use-blobs/
Make sure the virtual directory (~/App_Data directory as below example) where the image files are first uploaded are physically existence. When you publish the project, it may not be in the output files.
string fileSaveLocation = HttpContext.Current.Server.MapPath("~/App_Data"); CustomMultipartFormDataStreamProvider provider = new CustomMultipartFormDataStreamProvider(fileSaveLocation);

In ASP.NET, What could cause image manipulation to fail when using Fx or chrome, but succeed when using IE?

I am in a rather unusual pickle. I am modifying an image uploader and I thought I had it working. It needed to:
take a file from the client and upload it to server.
If file is an image, perform resizing operations on it.
If file is an image, create a thumbnail.
What I have works great when uploading images with Internet Explorer 8. But, when I upload images using Chrome, or Firefox3.+, the image gets uploaded but steps 2 and 3 are not performed. I don't get any server errors or anything. As 2 and 3 are steps that are performed on the server I have no idea how a change in browser could effect them.
I'm not sure if it has anything to do with my checking for whether the file is an image or not. But, for the sake of being thorough, here's the code I use:
try
{
string Filename = FileSystemUtilities.CleanupFilename(Path.GetFileName(hpf.FileName));
Filename = hpf.FileName;
string FileToSave = DestDir + Path.DirectorySeparatorChar + Path.GetFileName(Filename);
hpf.SaveAs(FileToSave);
bool IsImageFileType = ImageUtilities.IsImage(Filename, imageExtensions);
// below does not seem to execute when using non ie browser
// everything is smooth sailing when using ie.
if (IsImageFileType)
{
ImageUtilities.ResizeImageIfNecessary(FileToSave, mainMaxWidth, mainMaxHeight);
ImageUtilities.CreateThumbnail(FileToSave, thumbMaxWidth, thumbMaxHeight);
}
ValidOperation++;
sb.AppendFormat("{0} uploaded successfully<br/>", Filename);
}
Any thoughts? Why would server side code behave differently based on browser?
Edit: ImageUtilities.IsImage()
public static bool IsImage(string file, string[] imageExtensions)
{
file = Path.GetFullPath(file);
if (File.Exists(file))
{
string CurrentFileExtension = Path.GetExtension(file);
return imageExtensions.Count(x => x == CurrentFileExtension) > 0 ? true : false;
}
else
{
return false; //file doesn't exist
}
}
This difference would be caused by a difference in the filename sent by the browsers.
For example, some browsers include the full path.
Your ImageUtilities.IsImage function can't handle the filename sent by non-IE browsers.
EDIT: Your function is very wrong.
Change it to
return imageExtensions.Contains(Path.GetExtension(file),
StringComparer.OrdinalIgnoreCase);

Transfer file from Windows Mobile device to...anywhere

I can't seem to find a solution to this issue. I'm trying to get my Compact Framework application on Windows Mobile 6 to have the ability to move a file on its local filesystem to another system.
Here's the solutions I'm aware of:
FTP - Problem with that is most of
the APIs are way to expensive to use.
HTTP PUT - As far as I have been able to find, I can't use anonymous PUT with IIS7, and that's the web server the system is running. (An extreme workaround for this would be to use a different web server to PUT the file, and have that other system transfer it to the IIS system).
Windows share - I would need authentication on the shares, and I haven't seen that a way to pass this authentication through windows mobile.
The last resort would be to require that the devices be cradled to transfer these files, but I'd really like to be able to have these files be transferred wirelessly.
FTP: define "too expensive". Do you mean performance or byte overhead or dollar cost? Here's a free one with source.
HTTP: IIS7 certainly supports hosting web services or custom IHttpHandlers. You could use either for a data upload pretty easily.
A Windows Share simply requires that you to P/Invoke the WNet APIs to map the share, but it's not terribly complex.
I ended up just passing information to a web server via a PHP script.
The options provided above just didn't work out for my situation.
Here's the gist of it. I've got some code in there with progress bars and various checks and handlers unrelated to simply sending a file, but I'm sure you can pick through it. I've removed my authentication code from both the C# and the PHP, but it shouldn't be too hard to roll your own, if necessary.
in C#:
/*
* Here's the short+sweet about how I'm doing this
* 1) Copy the file from mobile device to web server by querying PHP script with paramaters for each line
* 2) PHP script checks 1) If we got the whole data file 2) If this is a duplicate data file
* 3) If it is a duplicate, or we didn't get the whole thing, it goes away. The mobile
* device will hang on to it's data file in the first case (if it's duplicate it deletes it)
* to be tried again later
* 4) The server will then process the data files using a scheduled task/cron job at an appropriate time
*/
private void process_attempts()
{
Uri CheckUrl = new Uri("http://path/to/php/script?action=check");
WebRequest checkReq = WebRequest.Create(CheckUrl);
try
{
WebResponse CheckResp = checkReq.GetResponse();
CheckResp.Close();
}
catch
{
MessageBox.Show("Error! Connection not available. Please make sure you are online.");
this.Invoke(new Close(closeme));
}
StreamReader dataReader = File.OpenText(datafile);
String line = null;
line = dataReader.ReadLine();
while (line != null)
{
Uri Url = new Uri("http://path/to/php/script?action=process&line=" + line);
WebRequest WebReq = WebRequest.Create(Url);
try
{
WebResponse Resp = WebReq.GetResponse();
Resp.Close();
}
catch
{
MessageBox.Show("Error! Connection not available. Please make sure you are online.");
this.Invoke(new Close(closeme));
return;
}
try
{
process_bar.Invoke(new SetInt(SetBarValue), new object[] { processed });
}
catch { }
process_num.Invoke(new SetString(SetNumValue), new object[] { processed + "/" + attempts });
processed++;
line = dataReader.ReadLine();
}
dataReader.Close();
Uri Url2 = new Uri("http://path/to/php/script?action=finalize&lines=" + attempts);
Boolean finalized = false;
WebRequest WebReq2 = WebRequest.Create(Url2);
try
{
WebResponse Resp = WebReq2.GetResponse();
Resp.Close();
finalized = true;
}
catch
{
MessageBox.Show("Error! Connection not available. Please make sure you are online.");
this.Invoke(new Close(closeme));
finalized = false;
}
MessageBox.Show("Done!");
this.Invoke(new Close(closeme));
}
In PHP (thoroughly commented for your benefit!):
<?php
//Get the GET'd values from the C#
//The current line being processed
$line = $_GET['line'];
//Which action we are doing
$action = $_GET['action'];
//# of lines in the source file
$totalLines = $_GET['lines'];
//If we are processing the line, open the data file, and append this new line and a newline.
if($action == "process"){
$dataFile = "tempdata/SOME_KIND_OF_UNIQUE_FILENAME.dat";
//open the file
$fh = fopen($dataFile, 'a');
//Write the line, and a newline to the file
fwrite($fh, $line."\r\n");
//Close the file
fclose($fh);
//Exit the script
exit();
}
//If we are done processing the original file from the C# application, make sure the number of lines in the new file matches that in the
//file we are transferring. An expansion of this could be to compare some kind of hash function value of both files...
if($action == "finalize"){
$dataFile = "tempdata/SOME_KIND_OF_UNIQUE_FILENAME.dat";
//Count the number of lines in the new file
$lines = count(file($dataFile));
//If the new file and the old file have the same number of lines...
if($lines == $totalLines){
//File has the matching number of lines, good enough for me over TCP.
//We should move or rename this file.
}else{
//File does NOT have the same number of lines as the source file.
}
exit();
}
if($action == "check"){
//If a file with this unique file name already exists, delete it.
$dataFile = "tempdata/SOME_KIND_OF_UNIQUE_FILENAME.dat";
if(file_exists($dataFile)){
unlink($dataFile);
}
}
?>

Categories