I am developing a music player in wp7 using silverlight.My end users don't have good internet connection when they are roaming. I want to give them an option to download music in their phone.
For that i have written a download manager which they can use to download music when they have wifi connection.
I ussue i am facing is that,My music is stored on my server and i am downloading the music on the phone using WebCLient class.
if size of file goes beyond 68 MB , my application gets OutOfMemory exception.
here is the code:
public void DownloadKirtan(KirtanViewModel kirtanVm,bool QueueItem=true)
{
{
if (kirtanVm.LocationPath != null)
{
WebClient webClient = new WebClient();
//webClient.AllowWriteStreamBuffering = false;
// webClient.AllowReadStreamBuffering = false;
if (QueueItem == false)
{
//App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload[kirtanVm] = webClient;
// App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Add(kirtanVm//
webClient = App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload[kirtanVm];
kirtanVm.IsDownloadedForOfflineViewing = "Started";
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
webClient.OpenReadAsync(kirtanVm.LocationPath, kirtanVm);
}
else if (!App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.ContainsKey(kirtanVm))
{
App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Add(kirtanVm, webClient);
kirtanVm.IsDownloadedForOfflineViewing = "Started";
webClient.OpenReadCompleted += new OpenReadCompletedEventHandler(webClient_OpenReadCompleted);
webClient.OpenReadAsync(kirtanVm.LocationPath, kirtanVm);
}
// webClient.DownloadProgressChanged += new DownloadProgressChangedEventHandler(webClient_DownloadProgressChanged);
}
}
}
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
KirtanViewModel kirtanVm = e.UserState as KirtanViewModel;
try
{
if (e.Cancelled == false)
{
if (e.Result != null)
{
((WebClient)sender).OpenReadCompleted -= webClient_OpenReadCompleted;
#region Isolated Storage Copy Code
IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication();
bool checkQuotaIncrease = IncreaseIsolatedStorageSpace(e.Result.Length);
if (checkQuotaIncrease)
{
string VideoFile = "";
VideoFile = GetUrlOfOfflineContent(kirtanVm);
using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(VideoFile, FileMode.Create, isolatedStorageFile))
{
long VideoFileLength = (long)e.Result.Length;
byte[] byteImage = new byte[VideoFileLength];
e.Result.Read(byteImage, 0, byteImage.Length);
isolatedStorageFileStream.Write(byteImage, 0, byteImage.Length);
kirtanVm.IsDownloadedForOfflineViewing = "True";
kirtanVm.DownloadSize = "Size=" + (VideoFileLength / 1000000).ToString() + " MB";
kirtanVm.DownloadProgress = "100%";
Settings.OfflineKirtanContents.Value.Add(kirtanVm);
AddRemoveKirtanInOfflineModels(kirtanVm, true);
}
#endregion
}
else
{
kirtanVm.IsDownloadedForOfflineViewing = "False";
App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
MessageBox.Show("There is not enough space in your phone to store this media. You need " + e.Result.Length + " bytes of storage.Please free some offline contents you have downloaded by going to Offline content managemnt screen in Settings Section and try again or increase the storage on your phone.");
}
// mediaFile.SetSource(isolatedStorageFileStream);
// mediaFile.Play();
// progressMedia.Visibility = Visibility.Collapsed;
}
}
}
catch (Exception ex)
{
kirtanVm.IsDownloadedForOfflineViewing = "Failed";
//App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
MessageBox.Show(ex.ToString());
}
}
The problem i am having is When i get e.result which is a stream object. To write to isolatedStoreFileStream, i have to read it again to byte array and then save it to isolatedstoragefile. This is inefficient, it consumes double memory. Memory of 60 MB (for 60MB file) by WebClient e.result stream object and then 60 MB to convert to array. So memory consumtion is 128 MB . Is there is better way to download big files and store it to IsolatedStorage>
UPDATE : I am now using following code using the chunck size , instead of reading all in memory but i still get out of memory error on large files 100 MB
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
KirtanViewModel kirtanVm = e.UserState as KirtanViewModel;
try
{
if (e.Cancelled == false)
{
if (e.Result != null)
{
((WebClient)sender).OpenReadCompleted -= webClient_OpenReadCompleted;
#region Isolated Storage Copy Code
bool checkQuotaIncrease = IncreaseIsolatedStorageSpace(e.Result.Length);
if (checkQuotaIncrease)
{
string VideoFile = "";
VideoFile = GetUrlOfOfflineContent(kirtanVm);
ThreadPool.QueueUserWorkItem( k =>{
using (IsolatedStorageFile isolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream isolatedStorageFileStream = new IsolatedStorageFileStream(VideoFile, FileMode.Create, isolatedStorageFile))
{
long VideoFileLength = (long)e.Result.Length;
using (BinaryWriter writer = new BinaryWriter(isolatedStorageFileStream))
{
Stream resourceStream = e.Result;//streamResourceInfo.Stream;
long length = resourceStream.Length;
byte[] buffer = new byte[32];
int readCount = 0;
using (BinaryReader reader = new BinaryReader(resourceStream))
{ // read file in chunks in order to reduce memory consumption and increase performance
while (readCount < length)
{
int actual = reader.Read(buffer, 0, buffer.Length);
readCount += actual;
writer.Write(buffer, 0, actual);
}
}
}
kirtanVm.IsDownloadedForOfflineViewing = "True";
kirtanVm.DownloadSize = "Size=" + (VideoFileLength / 1000000).ToString() + " MB";
kirtanVm.DownloadProgress = "100%";
Settings.OfflineKirtanContents.Value.Add(kirtanVm);
AddRemoveKirtanInOfflineModels(kirtanVm, true);
}
}
});
//byte[] byteImage = new byte[VideoFileLength];
//e.Result.Read(byteImage, 0, byteImage.Length);
// e.re
//ThreadPool.QueueUserWorkItem( k =>{
// isolatedStorageFileStream.Write(byteImage, 0, byteImage.Length);
//isolatedStorageFileStream.Close();
//Application.Current.RootVisual.Dispatcher.BeginInvoke( ()=>
// {
// kirtanVm.IsDownloadedForOfflineViewing = "True";
// kirtanVm.DownloadSize = "Size=" + (VideoFileLength / 1000000).ToString() + " MB";
// kirtanVm.DownloadProgress = "100%";
// Settings.OfflineKirtanContents.Value.Add(kirtanVm);
// AddRemoveKirtanInOfflineModels(kirtanVm, true);
// });
//});
//StreamWriter writer=new StreamWriter(isolatedStorageFileStream);
// writer.Write(e.Result);
// writer.Close();
// isolatedStorageFileStream.Write(
// e.Result.Write(
// isolatedStorageFileStream.Write(byteImage, 0, byteImage.Length);
// isolatedStorageFileStream.Close();
}
#endregion
else
{
kirtanVm.IsDownloadedForOfflineViewing = "False";
App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
MessageBox.Show("There is not enough space in your phone to store this media. You need " + e.Result.Length + " bytes of storage.Please free some offline contents you have downloaded by going to Offline content managemnt screen in Settings Section and try again or increase the storage on your phone.");
}
}
}
else
{
lock (App.ViewModel.LockForCancelForCurrentOfflineDownload)
{
if (App.ViewModel.cancelInitiatedByUserForCurrentOfflineDownload)
{
kirtanVm.IsDownloadedForOfflineViewing = "False";
// kirtanVm.IsOfflineDownloadCancelled = false;
App.ViewModel.cancelInitiatedByUserForCurrentOfflineDownload = false;
}
else
{
if (kirtanVm.IsDownloadedForOfflineViewing == "Started")// don't queue things again
{
kirtanVm.IsDownloadedForOfflineViewing = "Failed";
// bool ItemExist = App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Any(k => k.Key.Kirtan.KirtanId == kirtanVm.Kirtan.KirtanId);
DownloadKirtan(kirtanVm, false);
}
}
}
}
}
catch (Exception ex)
{
kirtanVm.IsDownloadedForOfflineViewing = "Failed";
//App.ViewModel.ActiveInstancesOfWebClientForKirtanDownload.Remove(kirtanVm);
MessageBox.Show(ex.ToString());
}
}
Thanks
Verinder
Finally i found the solution
turn on webClient.AllowReadStreamBuffering = false; in the above code which i shared and then in
void webClient_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e) {} method.. Remove all refrences to e.Result.Length. When using no buffering solution you cannot know the stream size in advance
long length = resourceStream.Length;
byte[] buffer = new byte[1024];
int readCount = 0;
using (BinaryReader reader = new BinaryReader(resourceStream)) { // read file in chunks in order to reduce memory consumption and increase performance
while (true)
{
int actual = reader.Read(buffer, 0, 1024);
if(actual==0)
{
break;
}
else
{
readCount += actual;
writer.Write(buffer, 0, actual);
}
}
}
Keep on reading the stream with 1024 bytes at a time unless you are done reading. This way WebClient will not buffer whole file in memory and max mem usage on your phone will be 1KB at a time. You can download Gigs of data like this. INcrease the buffer size to 1MB or 3 MB , if you want to read faster.
Make sure you run this code in ThreadPool instead of user thread
You need to write it one chunk at a time by reading into a 4KB (or so) array and writing that to isolated storage until you run out of data.
Related
I'm trying to limit file download a file at a time using System.Windows.Forms.WebBrowser or System.Windows.Controls.WebBrowser.
I try to catch this at System.Windows.Forms.WebBrowser.FileDowload event, but can't know when the file is completely downloaded.
Is there any way to catch when is the file download completed at Windows.Forms.WebBrowser or Windows.Controls.WebBrowser.
Thanks.
void descargaCompleta(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// When the download is complete
descargando = false;
}
void solo1Descarga (object sender, System.EventArgs e)
{
System.Windows.Forms.WebBrowser web = (System.Windows.Forms.WebBrowser)sender;
// Ask for Mutex
descargas.WaitOne();
descargando = true;
while (descargando)
;
descargas.ReleaseMutex();
}
// FunciĆ³n que Modifica la GUI
private void UpadteTabs()
{
WindowsFormsHost contWeb = new WindowsFormsHost();
System.Windows.Forms.WebBrowser nav = new System.Windows.Forms.WebBrowser();
// I set the Events to WebBrowser
nav.FileDownload += new EventHandler (solo1Descarga);
nav.DocumentCompleted += new WebBrowserDocumentCompletedEventHandler (descargaCompleta);
}
Note: "download a file at a time" means that a download single can be at the same time.
For example, if you are trying to download a mega file and an image, 1 of the files needs to wait to the other finish
You can use WebReponse to download file
WebRequest request = WebRequest.Create(fileUri);
WebResponse response = request.GetResponse();
DownLoadFile(response, response.ContentLength);
private void DownLoadFile(HttpWebResponse response, long contentLength)
{
var respContentType = response.ContentType.ToUpper();
using (var fileStream = File.OpenWrite(fileName))
{
using (var responseStream = response.GetResponseStream())
{
var buffer = new byte[1024];
int bytesRead;
int offset = 0;
//Thread.Sleep(100);
while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) != 0)
{
fileStream.Write(buffer, 0, bytesRead);
offset += bytesRead;
}
if (offset != response.ContentLength)
{
//return string.Format("Only downloaded {0} of {1} bytes", offset, response.ContentLength));
throw new ApplicationException(string.Format("Only downloaded {0} of {1} bytes",
offset, response.ContentLength));
}
}
}
}
Alternativly write a while loop to read the binary data
public void DownloadFile(Uri uri, string filePathToSave)
{
var downloadedData = webClient.DownloadData(uri);
while (downloadedData.Length == 0)
{
Thread.Sleep(2000);
downloadedData = webClient.DownloadData(uri);
}
Stream file = File.Open(filePathToSave, FileMode.Create);
file.Write(downloadedData, 0, downloadedData.Length);
file.Close();
}
The idea is that if I selected more than one file then upload one file when, then when it finishes to upload then upload the next one and so on.
I changed the filedialoug to be able to select multi:
private void btnBrowse_Click(object sender, EventArgs e)
{
openFileDialog1.Multiselect = true;
if (this.openFileDialog1.ShowDialog() != DialogResult.Cancel)
this.txtUploadFile.Text = this.openFileDialog1.FileName;
}
I can select more than one file the problem is that when using a breakpoint I see on this.txtUploadFile.Text one file only the first I selected. txtUploadFile is a textBox.
The first thing is: how can I see on the textBox(txtUploadFile) all the selected files and not only one? Or how can I get some indication that all the files I selected really selected? Maybe to show somehow in the textBox something that all files selected?
The important part is: how do I upload all the selected files one by one?
This is the button click event that starting the files uploading:
private void btnUpload_Click(object sender, EventArgs e)
{
if(this.ftpProgress1.IsBusy)
{
this.ftpProgress1.CancelAsync();
this.btnUpload.Text = "Upload";
}
else
{
FtpSettings f = new FtpSettings();
f.Host = this.txtHost.Text;
f.Username = this.txtUsername.Text;
f.Password = this.txtPassword.Text;
f.TargetFolder = this.txtDir.Text;
f.SourceFile = this.txtUploadFile.Text;
f.Passive = this.chkPassive.Checked;
try
{
f.Port = Int32.Parse(this.txtPort.Text);
}
catch { }
this.toolStripProgressBar1.Visible = true;
this.ftpProgress1.RunWorkerAsync(f);
this.btnUpload.Text = "Cancel";
}
}
I also have a backgroundworker with a progressBar so each file that is uploading the progressBar should show the progress of the uploading today it's showing for one file since I can upload only one file:
private void ftpProgress1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.toolStripStatusLabel1.Text = e.UserState.ToString(); // The message will be something like: 45 Kb / 102.12 Mb
this.toolStripProgressBar1.Value = Math.Min(this.toolStripProgressBar1.Maximum, e.ProgressPercentage);
}
private void ftpProgress1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Error != null)
MessageBox.Show(e.Error.ToString(), "FTP error");
else if(e.Cancelled)
this.toolStripStatusLabel1.Text = "Upload Cancelled";
else
this.toolStripStatusLabel1.Text = "Upload Complete";
this.btnUpload.Text = "Upload";
this.toolStripProgressBar1.Visible = true;
}
And last the class of the FTP uploader:
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
namespace FTP_ProgressBar
{
public partial class FtpProgress : BackgroundWorker
{
public static string ConnectionError;
private FtpSettings f;
public FtpProgress()
{
InitializeComponent();
}
public FtpProgress(IContainer container)
{
container.Add(this);
InitializeComponent();
}
private void FtpProgress_DoWork(object sender, DoWorkEventArgs e)
{
try
{
BackgroundWorker bw = sender as BackgroundWorker;
f = e.Argument as FtpSettings;
string UploadPath = String.Format("{0}/{1}{2}", f.Host, f.TargetFolder == "" ? "" : f.TargetFolder + "/", Path.GetFileName(f.SourceFile));
if (!UploadPath.ToLower().StartsWith("ftp://"))
UploadPath = "ftp://" + UploadPath;
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(UploadPath);
request.UseBinary = true;
request.UsePassive = f.Passive;
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Timeout = 300000;
request.Credentials = new NetworkCredential(f.Username, f.Password);
long FileSize = new FileInfo(f.SourceFile).Length;
string FileSizeDescription = GetFileSize(FileSize);
int ChunkSize = 4096, NumRetries = 0, MaxRetries = 50;
long SentBytes = 0;
byte[] Buffer = new byte[ChunkSize];
using (Stream requestStream = request.GetRequestStream())
{
using (FileStream fs = File.Open(f.SourceFile, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
int BytesRead = fs.Read(Buffer, 0, ChunkSize);
while (BytesRead > 0)
{
try
{
if (bw.CancellationPending)
return;
requestStream.Write(Buffer, 0, BytesRead);
SentBytes += BytesRead;
string SummaryText = String.Format("Transferred {0} / {1}", GetFileSize(SentBytes), FileSizeDescription);
bw.ReportProgress((int)(((decimal)SentBytes / (decimal)FileSize) * 100), SummaryText);
}
catch (Exception ex)
{
Debug.WriteLine("Exception: " + ex.ToString());
if (NumRetries++ < MaxRetries)
{
fs.Position -= BytesRead;
}
else
{
throw new Exception(String.Format("Error occurred during upload, too many retries. \n{0}", ex.ToString()));
}
}
BytesRead = fs.Read(Buffer, 0, ChunkSize);
}
}
}
using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
System.Diagnostics.Debug.WriteLine(String.Format("Upload File Complete, status {0}", response.StatusDescription));
}
catch (WebException ex)
{
switch (ex.Status)
{
case WebExceptionStatus.NameResolutionFailure:
ConnectionError = "Error: Please check the ftp address";
break;
case WebExceptionStatus.Timeout:
ConnectionError = "Error: Timout Request";
break;
}
}
}
public static string GetFileSize(long numBytes)
{
string fileSize = "";
if(numBytes > 1073741824)
fileSize = String.Format("{0:0.00} Gb", (double)numBytes / 1073741824);
else if(numBytes > 1048576)
fileSize = String.Format("{0:0.00} Mb", (double)numBytes / 1048576);
else
fileSize = String.Format("{0:0} Kb", (double)numBytes / 1024);
if(fileSize == "0 Kb")
fileSize = "1 Kb";
return fileSize;
}
}
public class FtpSettings
{
public string Host, Username, Password, TargetFolder, SourceFile;
public bool Passive;
public int Port = 21;
}
}
It is a bit long, but I couldn't find a way to narrow the code since it's all connected to each other.
What I need it to do is if I selected more than one file make something like a queue and automatic upload one file when finished upload the next file and so on until the last file.
Today I can upload only one file. Single file each time.
I need that it will automatically upload all the selected files. Using the progressBar and the backgroundworker like it is now.
Here is a procedure how to upload multiple files from the directory where ftp - an address of ftp server:
private void uploadFTP(DirectoryInfo d, string ftp)
{
FileInfo[] flist = d.GetFiles();
if (flist.GetLength(0) > 0)
{
foreach (FileInfo txf in flist)
{
FtpWebRequest request = (FtpWebRequest)FtpWebRequest.Create(ftp + txf.Name);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential("username", "password");
request.UsePassive = true;
request.UseBinary = true;
request.KeepAlive = false;
FileStream stream = File.OpenRead(txf.FullName);
byte[] buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
stream.Close();
Stream reqStream = request.GetRequestStream();
reqStream.Write(buffer, 0, buffer.Length);
reqStream.Close();
txf.Delete();
}
}
}
You should be using
OpenFileDialog1.FileNames
And iterate on those elements to upload them one by one.
FileNames Property
today while doing a logic for downloading large size files, i am facing an error. For downloading what my logic is am spltting the file into 10KB chunks and then integrating it and is downloading.While downloading what happening is like each time am reducing the total size by 10KB , but once the remaining length is less than 10KB my download is getting interuppted. Please find the below code of mine and let me know if any change needed in my logic.
protected void btnDownload_Click(object sender, EventArgs e)
{
SPSecurity.RunWithElevatedPrivileges(delegate()
{
System.IO.Stream iStream = null;
// Buffer to read 10K bytes in chunk:
byte[] buffer = new Byte[10000];
// Length of the file:
int length;
// Total bytes to read:
long dataToRead;
// Identify the file to download including its path.
string filepath = "C:\\Users\\GZT00000000000001020\\Desktop\\123.zip";
// Identify the file name.
string filename = System.IO.Path.GetFileName(filepath);
try
{
iStream = new System.IO.FileStream(filepath, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read);
dataToRead = iStream.Length;
Page.Response.ContentType = "application/octet-stream";
Page.Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
// Read the bytes.
while (dataToRead > 0)
{
// Verify that the client is connected.
if (Page.Response.IsClientConnected)
{
if( (dataToRead < 10000) && (dataToRead!=-1))
{
length = (int)dataToRead;
buffer = new Byte[length];
dataToRead = -1;
}
else
{
// Read the data in buffer.
length = iStream.Read(buffer, 0, 10000);
}
// Write the data to the current output stream.
Page.Response.OutputStream.Write(buffer, 0, length);
// Flush the data to the HTML output.
Page.Response.Flush();
if (dataToRead > 10000)
{
buffer = new Byte[10000];
dataToRead = dataToRead - length;
}
else if(dataToRead!=-1)
{
length =(int)dataToRead ;
buffer = new Byte[length];
}
}
else
{
//prevent infinite loop if user disconnects
dataToRead = -1;
}
}
}
catch (Exception ex)
{
// Trap the error, if any.
Page.Response.Write("Error : " + ex.Message);
}
finally
{
if (iStream != null)
{
//Close the file.
iStream.Close();
}
Page.Response.Close();
}
});
}
You don't call iStream.Read in the branch where dataToRead < 10000
I have a bit of code that uses a Task to transfer a file over a web service. However if the network drops or a timeout occurs it results in an incomplete file. I'm really trying to make this service as reliable as possible, but I'm not really sure where to even really start to add code that will trap when a chunk was not sent, and then attempt to resend it multiple times but also not send the next chunk until that is done. And maybe if it cannot resend that chunk over X times, fail completely and log an event.
Can anyone suggest anything?
Action<Guid, string> action = (smGuid, pubAttFullPath) =>
{
try
{
//Set filename from object
string FileName;
FileName = System.IO.Path.GetFileName(pubAttFullPath.ToString());
//Declare Web Service
TransferFile.TransferFileSoapClient ws_TransferFile = new TransferFile.TransferFileSoapClient();
//
bool transfercompleted = false;
using (FileStream fs = new FileStream(
pubAttFullPath.ToString(),
FileMode.Open,
FileAccess.Read,
FileShare.Read))
{
//Declare Buffers and Counts
byte[] buffer = new byte[49152];
long fileSize = fs.Length;
long totalReadCount = 0;
int readCount;
float percentageComplete = 0;
//Loop and copy file until it changes to not exactly the same byte count as the buffer
//which means the file is about to complete.
while ((readCount = fs.Read(buffer, 0, buffer.Length)) > 0)
{
if (!transfercompleted)
{
totalReadCount += readCount;
byte[] bytesToTransfer;
if (readCount == buffer.Length)
{
//Copy bytes until buffer is different
bytesToTransfer = buffer;
ws_TransferFile.WriteBinaryFile("ABCD", bytesToTransfer, FileName);
percentageComplete = (totalReadCount / (float)fileSize * 100);
percentageComplete = (float)Math.Round(percentageComplete, 2, MidpointRounding.ToEven);
//Update progress to DB
InsertProgress.InsertProgressSoapClient ws_InsertProgress = new InsertProgress.InsertProgressSoapClient();
if (percentageComplete == 10.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 10.0);
}
if (percentageComplete == 20.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 20.0);
}
if (percentageComplete == 30.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 30.0);
}
if (percentageComplete == 40.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 40.0);
}
if (percentageComplete == 50.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 50.0);
}
if (percentageComplete == 60.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 60.0);
}
if (percentageComplete == 70.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 70.0);
}
if (percentageComplete == 80.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 80.0);
}
if (percentageComplete == 90.00)
{
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 90.0);
}
}
else
{
// Only a part is requred to upload,
// copy that part.
List<byte> b = new List<byte>(buffer);
bytesToTransfer = b.GetRange(0, readCount).ToArray();
ws_TransferFile.WriteBinaryFile("ABCD", bytesToTransfer, FileName);
percentageComplete = 100;
//Insert Progress as complete
InsertProgress.InsertProgressSoapClient ws_InsertProgress = new InsertProgress.InsertProgressSoapClient();
ws_InsertProgress.InsertProgressService(smGuid.ToString(), 100);
transfercompleted = true;
fs.Close();
break;
}
}
}
}
}
catch (Exception ex)
{
EventLog.WriteEntry("Application", ex.Message.ToString(), EventLogEntryType.Error);
}
Web service is bad idea for file transfer. I used it few times, but amount of additional data transfered is making size of transfered data 1,5-2 times bigger then sending file using simple handler. Handler will allow you to the same without issues. It causes al lot of problem with proper progress handling and resume. You realy should reconsider using httphandler. If you like to use webservice, here is good example:
http://msdn.microsoft.com/en-us/library/ms172362%28v=vs.85%29.aspx
If you decide to use IHttpHandler see:
http://msdn.microsoft.com/en-us/library/ms228090%28v=vs.100%29.aspx
And then you can use following piece of code to proper handle retry/resume:
using (Stream stream = new FileStream(
pubAttFullPath.ToString(),
FileMode.Open,
FileAccess.Read,
FileShare.Read))
{
context.Response.AddHeader("Accept-Ranges", "bytes");
context.Response.Buffer = false;
if (context.Request.Headers["Range"] != null)
{
context.Response.StatusCode = 206;
string[] range = context.Request.Headers["Range"].Split(new[] { '=', '-' });
startBytes = Convert.ToInt32(range[1]);
}
int dataToRead = size - startBytes;
context.Response.ContentType = "application/octet-stream";
context.Response.AddHeader("Content-Length", dataToRead.ToString());
context.Response.AddHeader("Connection", "Keep-Alive");
context.Response.AddHeader("Content-Disposition", "attachment; filename=" + HttpUtility.UrlEncode(fileName, Encoding.UTF8));
if (startBytes > 0)
{
context.Response.AddHeader("Content-Range", string.Format(" bytes {0}-{1}/{2}", startBytes, size - 1, size));
stream.Seek(startBytes, SeekOrigin.Begin);
}
while (dataToRead > 0)
{
// Verify that the client is connected.
if (context.Response.IsClientConnected)
{
// Read the data in buffer.
int length = stream.Read(buffer, 0, buffer.Length);
// Write the data to the current output stream.
context.Response.OutputStream.Write(buffer, 0, length);
// Flush the data to the HTML output.
context.Response.Flush();
dataToRead = dataToRead - length;
}
else
{
// prevent infinite loop if user disconnects
dataToRead = -1;
}
}
}
I have a Network File Transfer here. It works fine and great. But I just had a problem on how to lessen my CPU usage to 5% in transferring small files like 200kb and below.
All the large file I transfer just had a 0 - 2% of CPU usage. But when I tried to transfer a small file it bursts to 7% to 20%. How could that be?
Any suggestions?
Here's my code...
For my Client:
private void SendFiles(){
try
{
FileInfo file;
//Checks first if it is connected
if (tcpClient.Connected == false)
{
//Create new instance
tcpClient = new TcpClient();
// Connect the TCP client to the specified IP and port
tcpClient.Connect(txtIP.Text, Convert.ToInt32(txtPort.Text));
}
// Get a stream connected to the server
streamSend = tcpClient.GetStream();
// byte[] byteSend = new byte[tcpClient.ReceiveBufferSize];
byte[] byteSend = new byte[byteLength];
int listCount = arrList.Count - indxItems;
byteSend = System.Text.Encoding.ASCII.GetBytes(listCount.ToString().PadRight(byteLength).ToCharArray());
// Write the sequence of bytes (the count of the items in the listbox) to the network stream
streamSend.Write(byteSend, 0, byteSend.Length);
while (indxItems < arrList.Count)
{
fName = arrList[indxItems].ToString();
// grab file info
file = new System.IO.FileInfo(fName);
SendFile(fName, streamSend, byteSend);
Thread.Sleep(1000);
minimizeMemory();
indxItems++;
}
if (indxItems == arrList.Count)
if (MessageBox.Show("Done Sending!\r\nClearing list of sent file(s) and/or folder(s)", "Done Sending", MessageBoxButtons.OK) == DialogResult.OK)
{
this.Invoke(new Clear(this.ClearList), new object[] { true });
tcpClient.Close();
streamSend.Close();
readFile.Close();
}
}
catch (SocketException exMessage)
{
MessageBox.Show("Can't send, Server not available.\r\nSearch for another connection.", "Sending Error!");
this.Invoke(new Clear(this.ClearList), new object[] { true });
}
}
For my SendFile Method:
private void SendFile(String fName, NetworkStream netStream, byte[] byteSend)
{
minimizeMemory();
try
{
readFile = new FileStream(fName, FileMode.Open, FileAccess.Read);
// Get file info about the opened file
FileInfo fileInfo = new FileInfo(fName);
// Get and store the file size
long FileSize = Convert.ToInt64(fileInfo.Length);
// Get and store the file name
String FileName = fileInfo.Name;
byteSend = System.Text.Encoding.ASCII.GetBytes(FileName.PadRight(byteLength).ToCharArray());
// Write the sequence of bytes (file name) to the network stream
netStream.Write(byteSend, 0, byteSend.Length);
byteSend = System.Text.Encoding.ASCII.GetBytes(FileSize.ToString().PadRight(byteLength).ToCharArray());
// Write the sequence of bytes (file size) to the network stream
netStream.Write(byteSend, 0, byteSend.Length);
Thread.Sleep(50);
int byteRead = 0;
// Define the buffer size
byte[] downBuffer = new byte[byteLength];
// Loop through the file strem of the local file
while (((byteRead = readFile.Read(downBuffer, 0, downBuffer.Length)) > 0) && (streamSend != null))
{
// Write the data that composes the file to the network stream
netStream.Write(downBuffer, 0, byteRead);
uploadSize += byteRead;
Thread.Sleep(200);
}
minimizeMemory();
}
catch (SocketException se)
{
MessageBox.Show("Can't connect, Server not available.", "Connection Error!");
this.Invoke(new Clear(this.ClearList), new object[] { true });
}
catch (InvalidOperationException ioe)
{
MessageBox.Show("Action might be closed by the Server.", "Connection Error!");
this.Invoke(new Clear(this.ClearList), new object[] { true });
}
catch (IOException ioe)
{
MessageBox.Show("Action might be closed by the Server.", "Connection Error!");
this.Invoke(new Clear(this.ClearList), new object[] { true });
}
}
And here's for my Server:
private void DownloadFromConnection(TcpClient tcpConn, String ipadd)
{
try
{
bool disconnected = false;
fCount = 0;
success = 0;
byteLength = 1024 * 100;
// For holding the number of bytes we are reading at one time from the stream
int bytesSize = 0;
// String to hold the FilePath
String filePath = String.Empty;
// The buffer that holds the data received from the client
byte[] downloadBuffer = new byte[byteLength];
// Receive the stream and store it in a NetworkStream object
strRemote = tcpConn.GetStream();
// Read the first buffer(byteLength bytes) from the stream - which represents the count of file(s)/folder(s) to be received.
bytesSize = strRemote.Read(downloadBuffer, 0, byteLength);
fCount = Convert.ToInt32(System.Text.Encoding.ASCII.GetString(downloadBuffer, 0, bytesSize).Trim());
if (fCount > 0)
{
for (int i = fCount; i > 0; i--)
{
disconnected = DownloadFilesFromConnection(bytesSize, ipadd, filePath, downloadBuffer);
if (disconnected)
break;
Thread.Sleep(50);
minimizeMemory();
}
if (success == fCount)
{
MessageBox.Show(fCount + " file(s) from " + ipadd + " successfully received.", "File Received");
}
}
isReceiving = false;
}
catch
{
this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { false, 0 });
}
finally
{
// This part of the method will fire no matter wether an error occured in the above code or not
if (strLocal != null && strRemote != null)
{
// Close the streams
strLocal.Close();
strRemote.Close();
}
}
}
For DownloadFilesFromConnection Method:
private bool DownloadFilesFromConnection(int byteSize, String ipAddress, String filepath, byte[] downloadBuffer)
{
minimizeMemory();
// Cancels receiving operations if client is disconnected
bool clientDisconnected = false;
try
{
// The buffer that holds the data received from the client
downloadBuffer = new byte[byteLength];
this.Invoke(new IsCurrReceivingF(this.IsCurrRecF), new object[] { true });
// Read the buffer (byteLength bytes) from the stream - which represents the file name
byteSize = strRemote.Read(downloadBuffer, 0, byteLength);
// Convert the stream to string and store the file name
FName = System.Text.Encoding.ASCII.GetString(downloadBuffer, 0, byteSize).Trim();
// String to hold the FilePath
filepath = destPath + "\\" + FName;
this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { true, 1 });
// Set the file stream to the path C:\ plus the name of the file that was on the sender's computer
strLocal = new FileStream(filepath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
// Read the next buffer (byteLength bytes) from the stream - which represents the file size
byteSize = strRemote.Read(downloadBuffer, 0, byteLength);
// Convert the file size from bytes to string and then to long (Int64)
long FileSize = Convert.ToInt64(System.Text.Encoding.ASCII.GetString(downloadBuffer, 0, byteSize).Trim());
long size = 0;
int update = 0;
// From now on we read everything that's in the stream's buffer because the file content has started
while ((byteSize = strRemote.Read(downloadBuffer, 0, downloadBuffer.Length)) >= 0 && size != FileSize)
{
if (byteSize == 0 && size != FileSize)
{
ForceStopDownload(strLocal, strRemote, ipAddress);
this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { false, 1 });
if (File.Exists(filepath))
{
File.Delete(filepath);
this.Invoke(new UpdateStatusCallback(this.UpdateStatus), new object[] { false, 1 });
this.Invoke(new UpdateProgressCallback(this.UpdateProgress), new object[] { 0, 0 });
this.Invoke(new UpdateProgressFileCallback(this.UpdateProgressFile), new object[] { 0, 0 });
FName = String.Empty;
}
MessageBox.Show("Only " + success + " file(s) successfully copied from " + ipAddress + ".", "File Transferring Canceled");
clientDisconnected = true;
break;
}
else
{
update++;
// Write the data to the local file stream
strLocal.Write(downloadBuffer, 0, byteSize);
size += byteSize;
downloadedSize += byteSize;
if (update == 2)
{
// Update the progressbar by passing the file size and how much we downloaded so far to UpdateProgress()
this.Invoke(new UpdateProgressCallback(this.UpdateProgress), new object[] { strLocal.Length, FileSize });
this.Invoke(new UpdateProgressFileCallback(this.UpdateProgressFile), new object[] { strLocal.Length, FileSize });
update = 0;
}
if (size == FileSize)
{
// Write the status to the log textbox on the form (txtLog)
this.Invoke(new UpdateProgressCallback(this.UpdateProgress), new object[] { 0, 0 });
this.Invoke(new UpdateProgressFileCallback(this.UpdateProgressFile), new object[] { 0, 0 });
FName = String.Empty;
success++;
break;
}
}
Thread.Sleep(1);
}
if (clientDisconnected)
return clientDisconnected;
}
catch (ObjectDisposedException ode)
{
;
}
catch (InvalidOperationException ioe)
{
;
}
return clientDisconnected;
}
I know my codes are not that good. I'm new in C# but hoping to have answers to my questions.=)