I've written a small .ashx handler in C#, with straight-forward logic:
Generate a random payload string of 54 kilobytes.
Get a unique filename to store the data.
Write the string to file in async manner.
Read back from that file in async manner.
Send that string back to response stream.
The idea is to throw multiple concurrent requests to the above handler using apache-bench, so that I can compare ASP.NET 4.5 against other frameworks (like nodejs) for a large-size app I'm going to develop which is heavily I/O bound. Here is the code for the Handler:
public class Handler : System.Web.IHttpHandler
{
private StringBuilder payload = null;
private async void processAsync()
{
var r = new Random ();
//generate a random string of 108kb
payload=new StringBuilder();
for (var i = 0; i < 54000; i++)
payload.Append( (char)(r.Next(65,90)));
//create a unique file
var fname = "";
do{fname = "c:\\source\\csharp\\asyncdemo\\" + r.Next (1, 99999999).ToString () + ".txt";
} while(File.Exists(fname));
//write the string to disk in async manner
using(FileStream fs = File.Open(fname,FileMode.CreateNew,FileAccess.ReadWrite))
{
var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
await fs.WriteAsync (bytes,0,bytes.Length);
fs.Close ();
}
//read the string back from disk in async manner
payload = new StringBuilder ();
StreamReader sr = new StreamReader (fname);
payload.Append(await sr.ReadToEndAsync ());
sr.Close ();
//File.Delete (fname); //remove the file
}
public void ProcessRequest (HttpContext context)
{
Task task = new Task(processAsync);
task.Start ();
task.Wait ();
//write the string back on the response stream
context.Response.ContentType = "text/plain";
context.Response.Write (payload.ToString());
}
public bool IsReusable
{
get {
return false;
}
}
}
The trouble is that the Handler runs perfectly when I compile it and run from my browser once. But when I send concurrent requests to it using ab, about half the requests get dropped. Not only that, but its way too slow compared to a similar script I've written in node.js - I've tested it both on IIS-7/Windows and Mono/Linux environments. Do you suggest ASP.NET is inherently slower compared to node.js to handle heavy async I/O load?
I've reimplemented my code in the following manner, and it works now:
private StringBuilder payload = null;
private async void processAsync()
{
var r = new Random (DateTime.Now.Ticks.GetHashCode());
//generate a random string of 108kb
payload=new StringBuilder();
for (var i = 0; i < 54000; i++)
payload.Append( (char)(r.Next(65,90)));
//create a unique file
var fname = "";
do
{
//fname = #"c:\source\csharp\asyncdemo\" + r.Next (1, 99999999).ToString () + ".txt";
fname = r.Next (1, 99999999).ToString () + ".txt";
} while(File.Exists(fname));
//write the string to disk in async manner
using(FileStream fs = new FileStream(fname,FileMode.CreateNew,FileAccess.Write, FileShare.None,
bufferSize: 4096, useAsync: true))
{
var bytes=(new System.Text.ASCIIEncoding ()).GetBytes (payload.ToString());
await fs.WriteAsync (bytes,0,bytes.Length);
fs.Close ();
}
//read the string back from disk in async manner
payload = new StringBuilder ();
//FileStream ;
//payload.Append(await fs.ReadToEndAsync ());
using (var fs = new FileStream (fname, FileMode.Open, FileAccess.Read,
FileShare.Read, bufferSize: 4096, useAsync: true)) {
int numRead;
byte[] buffer = new byte[0x1000];
while ((numRead = await fs.ReadAsync (buffer, 0, buffer.Length)) != 0) {
payload.Append (Encoding.Unicode.GetString (buffer, 0, numRead));
}
}
//fs.Close ();
//File.Delete (fname); //remove the file
}
public void ProcessRequest (HttpContext context)
{
Task task = new Task(processAsync);
task.Start ();
task.Wait ();
//write the string back on the response stream
context.Response.ContentType = "text/plain";
context.Response.Write (payload.ToString());
}
Related
I'm trying to asynchronously upload file chunks from a .Net client to a php web server using HttpClient in C#.
I can chunk the file just fine, and upload those chunks to the remote server, but I'm not sure if this is really running asynchronously. Ideally, I would upload the chunks in parallel to maximize upload speed. My code is as follows:
//Call to chunk and upload file in another async method. I'm only showing the call here:
FileStream fileStream = new FileStream(fileNameIn, FileMode.Open, FileAccess.Read);
await ChunkFileAsync(fileStream, uploadFile.Name, url);
// To chunk the file
public static async Task<bool> ChunkFileAsync(FileStream fileStream, string fileName, string url)
{
int chunkSize = 102400; // Upload 1mb at a time
int totalChunks = (int)Math.Ceiling((double)fileStream.Length / chunkSize);
// Loop through the whole stream and send it chunk by chunk asynchronously;
bool retVal = true;
List<Task> tasks = new List<Task>();
try {
for (int i = 0; i < totalChunks; i++)
{
int startIndex = i * chunkSize;
int endIndex = (int)(startIndex + chunkSize > fileStream.Length ? fileStream.Length : startIndex + chunkSize);
int length = endIndex - startIndex;
byte[] bytes = new byte[length];
fileStream.Read(bytes, 0, bytes.Length);
Task task = SendChunkRequest(fileName, url, i, bytes);
tasks.Add(task);
retVal = true;
}
await Task.WhenAll(tasks);
}
catch (WebException e)
{
Console.WriteLine("ERROR - There was an error chunking the file before sending the request " + e.Message);
retVal = false;
}
return retVal;
}
//To upload chunks to remote server
public static async Task<bool> SendChunkRequest(string fileName, string url, int loopCounter, Byte[] bytes)
{
bool response = false;
try
{
ASCIIEncoding ascii = new ASCIIEncoding();
ByteArrayContent data = new ByteArrayContent(bytes);
data.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("multipart/form-data");
byte[] strParamsBytes = ascii.GetBytes(strVals);
HttpClient requestToServer = new HttpClient();
MultipartFormDataContent form = new MultipartFormDataContent();
form.Add(data, "file", fileName + loopCounter);
await requestToServer.PostAsync(url, form);
requestToServer.Dispose();
response = true;
}
catch (Exception e)
{
Console.WriteLine("There was an exception: " + e);
}
return response;
}
If I upload a 100Mb file, I see all ten chunks uploaded to the server one at a time. Can I make this code more efficient? Any help is greatly appreciated.
I have a text file consisting of 21000 lines, i have an attribute and i need to search it in the .txt file and need to return a value from the same too. All code is done and tried async plus new thread but there is a five second lag during the button click . how can i remove the lag.
Tried on new unity and C#
public async void read()
{
string[] lines = await ReadAllLinesAsync("Assets/Blockchain Module/" + File + ".csv");
fields = null;
for (int j = 0; j < lines.Length; j++)
{
fields = lines[j].Split(',');
x[j] = System.Convert.ToDouble(fields[1]);
y[j] = System.Convert.ToDouble(fields[2]);
z[j] = System.Convert.ToDouble(fields[3]);
temp[j] = System.Convert.ToDouble(fields[4]);
}
}
public void Start()
{
Thread thread = new Thread(read);
thread.Start();
//gradient.Evaluate()
//var main = particleSystem.main;
//main.maxParticles = 200;
}
private const FileOptions DefaultOptions = FileOptions.Asynchronous | FileOptions.SequentialScan;
public static Task<string[]> ReadAllLinesAsync(string path) => ReadAllLinesAsync(path, Encoding.UTF8);
public static async Task<string[]> ReadAllLinesAsync(string path, Encoding encoding)
{
var lines = new List<string>();
// Open the FileStream with the same FileMode, FileAccess
// and FileShare as a call to File.OpenText would've done.
using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultBufferSize, DefaultOptions))
using (var reader = new StreamReader(stream, encoding))
{
string line;
while ((line = await reader.ReadLineAsync()) != null)
{
lines.Add(line);
}
}
return lines.ToArray();
}
Reading and parsing the file apparently costs 5 seconds on your system. I don't think reading it line by line is the fastest approach, but anyway, don't parse the file for each request.
Read it once on application startup, and cache it in an appropriate data type.
In general if your search is line based is better to read line one by one instead to read all the file:
using (StreamReader reader = new StreamReader("filename"))
{
while (true)
{
string line = await reader.ReadLineAsync();
if (line == null)
{
break;
}
//logic here...
}
}
I'm trying to download a file from my DropBox account.
I get an error with
var task = Task.Run((Func<Task>)Download("", "largetest.mpk", folderName));
The error:
Cannot convert type 'System.Threading.Tasks.Task' to 'System.Func<System.Threading.Tasks.Task>
private void button1_Click(object sender, EventArgs e)
{
string folderName = #"c:\dropboxTest\test.exe";
var task = Task.Run((Func<Task>)Download("", "largetest.mpk", folderName));
task.Wait();
}
async Task Download(string folder, string targetfile, string localPath)
{
var dbx = new DropboxClient(Form1.api);
var response = await dbx.Files.DownloadAsync(folder + "/" + targetfile);
ulong fileSize = response.Response.Size;
const int bufferSize = 1024 * 1024;
var buffer = new byte[bufferSize];
string folderName = #"C:\dropboxTest\teasdfst.exe";
using (var stream = await response.GetContentAsStreamAsync())
{
using (var localfile = new FileStream(folderName, FileMode.OpenOrCreate))
{
var length = stream.Read(buffer, 0, bufferSize);
while (length > 0)
{
localfile.Write(buffer, 0, length);
// Console.WriteLine(localfile.);
var percentage = 100 * (ulong)localfile.Length / fileSize;
// Update progress bar with the percentage.
// progressBar.Value = (int)percentage
//Console.WriteLine(percentage);
length = stream.Read(buffer, 0, bufferSize);
}
}
}
}
You should write your function like this
void Download(string folder, string targetfile, string localPath)
{
var dbx = new DropboxClient(Form1.api);
var response = dbx.Files.Download(folder + "/" + targetfile);
ulong fileSize = response.Response.Size;
const int bufferSize = 1024 * 1024;
var buffer = new byte[bufferSize];
string folderName = #"C:\dropboxTest\teasdfst.exe";
using (var stream = response.GetContentAsStream())
{
using (var localfile = new FileStream(folderName, FileMode.OpenOrCreate))
{
var length = stream.Read(buffer, 0, bufferSize);
while (length > 0)
{
localfile.Write(buffer, 0, length);
// Console.WriteLine(localfile.);
var percentage = 100 * (ulong)localfile.Length / fileSize;
// Update progress bar with the percentage.
// progressBar.Value = (int)percentage
//Console.WriteLine(percentage);
length = stream.Read(buffer, 0, bufferSize);
}
}
}
}
and then call with Task.Run like below
private void button1_Click(object sender, EventArgs e)
{
string folderName = #"c:\dropboxTest\test.exe";
var task = Task.Run(() => Download("", "largetest.mpk", folderName));
task.Wait(); // remove this if you don't want to block UI thread.
}
Hope this helps !!
You do not need to do all of that. No conversions, no casts, no Task.Run.
Just write
const string folderName = #"c:\dropboxTest\test";
private async void button1_Click(object sender, EventArgs e)
{
await Download("", "largetest.mpk", folderName);
}
Note that we directly await the method call. Note how the call to Task.Wait has been removed.
Note that except in the case of event handlers, which need to be void methods, async methods should return Task objects. This is very important, especially for proper exception handling. This is the only case where async void is proper.
Your Download method, which should be renamed to DownloadAsync by convention, already has the proper return type Task and should not be changed in that regard.
I have the following code to write a file. The problem is that if I want to overwrite the same file, it is "locked". The file is opened by another process.
using (FileStream fs = new FileStream("C:\\New\\" + fileName, FileMode.Create, FileAccess.ReadWrite))
using (StreamWriter str = new StreamWriter(fs))
{
str.Write(jsonFile);
str.Dispose();
str.Close();
}
I send a json string to an API, which then generates the file. So I guess it might be a problem in IIS.
EDIT:
By research I have still tried the following code, but which leads to the same result
using (FileStream fs = new FileStream("C:\\New\\" + fileName, FileMode.Create, FileAccess.ReadWrite))
{
StreamWriter sw = new StreamWriter(fs);
sw.Write(jsonFile);
fs.Flush();
fs.Close();
}
EDIT 2:
After reading the comments, it probably has nothing to do with the Filestream to itself. Here is more information about my application:
I have a WPF application which sends a post to my API through a ButtonClick. This is triggered as follows:
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
await Seal();
}
The Seal method says the following:
private async System.Threading.Tasks.Task Seal()
{
var result = await RequestManager.DoPost<bool>("FOO", foo);
}
The RequestManager says the following:
public static async Task<R> DoPost<R>(String route, Object payload, String contenttype)
{
var serializer = new JavaScriptSerializer();
route = route.StartsWith("/") ? route : "/" + route;
var content = new StringContent(serializer.Serialize(payload), Encoding.UTF8, contenttype);
var response = await client.PostAsync(RequestManager.API_URL + route, content);
if (response.StatusCode == HttpStatusCode.OK)
{
var result = await response.Content.ReadAsStringAsync();
return (R)serializer.Deserialize(result, typeof(R));
}
else
{
throw new ResponseException(response.StatusCode, response.Content.ReadAsStringAsync().Result);
}
}
I really do not know where my error is, or where the request must be closed.
This error could occur when multiple threads are attempting to write at the same file. Your code above works with a little modification
private static object lk = new object();
lock (lk)
{
using (FileStream fs = new FileStream("C:\\New\\" +fileName,FileMode.Create, FileAccess.ReadWrite))
using (StreamWriter str = new StreamWriter(fs))
{
str.Write(jsonFile);
str.Dispose();
str.Close();
}
}
I'm trying to receive a file using Web Api from a user and then convert the file into a FileStream without writing the file to a server(Must stay in memory).
I have code that will allow me to write it to the server but all attempts to put it into a FileStream without writing the file to the server have failed.
public class ReceiverController : ApiController
{
public Task<HttpResponseMessage> Upload()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType));
}
string root = HttpContext.Current.Server.MapPath("~/App_Data/");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
FileInfo fileInfo = new FileInfo(provider.FileData.First().LocalFileName);
string fileName = provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "");
if (!File.Exists(Path.Combine(root, fileName)))
{
File.Move(fileInfo.FullName, Path.Combine(root, fileName));
}
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
});
return task;
}
}
fileName.PostedFile.InputStream --- gives me Stream and I use the following code to convert it to byte array. Then byte array is converted to filestream according to the following code.
BinaryReader br = new BinaryReader(fileName);
bytary= br.ReadBytes((Int32)fileName.Length);
for(int i = 0; i < bytary.Length; i++)
{
fileStream.WriteByte(bytary[i]);
}