Don't Understand Secondary Role of Stream.Read() While Downloading File - c#

I made a button in WinForms to download file from FTP and it works fine but I don't understand what Stream.Read() method is doing in the while loop in the code below (in DownLoadFileFromFtp method):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private static void DownloadFileFromFtp (string serverURI, string pathToLocalFile)
{
FileStream localFileStream = File.Open(pathToLocalFile, FileMode.OpenOrCreate);
FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest.Create(serverURI);
ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile;
ftpRequest.Credentials = new NetworkCredential("demo", "password");
WebResponse ftpResponse = ftpRequest.GetResponse();
Stream ftpResponseStream = ftpResponse.GetResponseStream();
byte[] buffer = new byte[2048];
Console.WriteLine($"\nBuffer length is: {buffer.Length}");
int bytesRead = ftpResponseStream.Read(buffer, 0, buffer.Length);
while(bytesRead > 0)
{
localFileStream.Write(buffer, 0, buffer.Length);
bytesRead = ftpResponseStream.Read(buffer, 0, buffer.Length);
}
localFileStream.Close();
ftpResponseStream.Close();
}
private void Form1_Load(object sender, EventArgs e)
{
if(NetworkInterface.GetIsNetworkAvailable())
{
toolStripStatusLabel1.Text = "Connected";
}
else
{
toolStripStatusLabel1.Text = "Disconnected";
}
}
private void DownloadFile_Click(object sender, EventArgs e)
{
DownloadFileFromFtp("ftp://test.rebex.net/pub/example/readme.txt", #"C:\Users\nstelmak\Downloads\testFile.txt");
}
}
Can anybody explain what this piece of code doing (bytesRead = ftpResponseStream.Read(buffer, 0, buffer.Length);) in a while loop? If I don use this piece of code then it seems like download is infinite and the file began to be bloated.
Thanks in advance!

Comments in code
// Take initial read. The return value is number of bytes you had read from the ftp stream.
// it is not guarantee what that number will be
int bytesRead = ftpResponseStream.Read(buffer, 0, buffer.Length);
// Now you need to keep reading until the return value is '0'. If you call
// stream.read() and got '0', it means that you came to the end of stream
while(bytesRead > 0)
{
localFileStream.Write(buffer, 0, buffer.Length);
// here you're going to keep reading bytes in the loop
// until you come to the end of stream and can't read no more
bytesRead = ftpResponseStream.Read(buffer, 0, buffer.Length);
}

Related

Download a torrent file result is corrupt WP8

in an app I am making some of the files required are torrent files but i'm having an odd issue, whenever I download a torrent file through the app the files ends up corrupt and wont open in any torrent app, I used wptools to extract them to a pc and test it and still corrupt here is my code I can't see what 'im doing wrong, I am fairly new to using webclient. I assume it has something to do with the way im saving the file any help would be great thanks.
private void tbLink_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
string[] linkInfo = (sender as TextBlock).Tag as string[];
fileurl = linkInfo[0];
System.Diagnostics.Debug.WriteLine(fileurl);
WebClient client = new WebClient();
client.OpenReadCompleted += client_OpenReadCompleted;
client.OpenReadAsync(new Uri(fileurl), linkInfo);
client.AllowReadStreamBuffering = true;
}
async void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
string[] linkInfo = e.UserState as string[];
filetitle = linkInfo[1];
filesave = (filetitle);
var isolatedfile = IsolatedStorageFile.GetUserStoreForApplication();
using (IsolatedStorageFileStream stream = isolatedfile.OpenFile(filesave, System.IO.FileMode.Create))
{
byte[] buffer = new byte[e.Result.Length];
while (e.Result.Read(buffer, 0, buffer.Length) > 0)
{
stream.Write(buffer, 0, buffer.Length);
}
}
try
{
StorageFolder local = Windows.Storage.ApplicationData.Current.LocalFolder;
StorageFile torrentfile = await local.GetFileAsync(filesave);
Windows.System.Launcher.LaunchFileAsync(torrentfile);
}
catch (Exception)
{
MessageBox.Show("File Not Found");
}
This is incorrect:
byte[] buffer = new byte[e.Result.Length];
while (e.Result.Read(buffer, 0, buffer.Length) > 0)
{
stream.Write(buffer, 0, buffer.Length);
}
The Read method will return the number of bytes read, it can be less than buffer.Length. So the code should read:
int byteCount;
// Select an appropriate buffer size.
// This is a buffer, not space for the entire file.
byte[] buffer = new byte[4096];
while ((byteCount = e.Result.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, byteCount);
}
UPDATE: If the data is compressed, as in the question that you posted in your comment, then you can decompress the stream:
int byteCount;
byte[] buffer = new byte[4096];
using (GZipStream zs = new GZipStream(e.Result, CompressionMode.Decompress))
{
while ((byteCount = zs.Read(buffer, 0, buffer.Length)) > 0)
{
stream.Write(buffer, 0, byteCount);
}
}
Note that I have not tested this code, I'm assuming that e.Result is a stream.

Error 550 File unavailable when downloading from FTP

I had a little problem while downloading a file from FTP. I'm getting a error that says "the remote server returned an error (550) File unavailable (e.g, file not found, no access)."
The code is below:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btntamam_Click(object sender, EventArgs e)
{
FtpWebRequest FTP;
try
{
FileStream SR = new FileStream("C:\\asd" + "\\list.gz", FileMode.Create);
FTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://xx.x.xx.xxx/" + "list.gz"));
FTP.Credentials = new NetworkCredential("username", "pass");
FTP.Method = WebRequestMethods.Ftp.DownloadFile;
FTP.UseBinary = true;
FtpWebResponse response = (FtpWebResponse)FTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];
readCount = ftpStream.Read(buffer, 0, bufferSize);
while (readCount > 0)
{
SR.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}
ftpStream.Close();
SR.Close();
response.Close();
MessageBox.Show("File Downloaded!");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Error");
}
}
}
}
The code seems to be working, but it's unable to download the file I want. I've searched a lot but could not find any solutions..

Download to file and save to byte array

I am trying to download a file to my computer and in the same time save it to Byte Array:
try
{
var req = (HttpWebRequest)HttpWebRequest.Create(url);
var fileStream = new FileStream(filePath,
FileMode.Create, FileAccess.Write, FileShare.Write);
using (var resp = req.GetResponse())
{
using (var stream = resp.GetResponseStream())
{
byte[] buffer = new byte[0x10000];
int len;
while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
{
//Do with the content whatever you want
// ***YOUR CODE***
MemoryStream memoryStream = new MemoryStream();
if (len > 0)
{
memoryStream.Write(buffer, 0, len);
len = stream.Read(buffer, 0, buffer.Length);
}
file = memoryStream.ToArray();
fileStream.Write(buffer, 0, len);
}
}
}
fileStream.Close();
}
catch (Exception exc) { }
And i noticed that it's not download all the file with this.
I wan to do it because i want to download a file and in the same time work with it.
Any idea why this problem happen?
There is a much easier way to get the file bytes by using the System.Net.WebClient.WebClient():
private static byte[] DownloadFile(string absoluteUrl)
{
using (var client = new System.Net.WebClient())
{
return client.DownloadData(absoluteUrl);
}
}
Usage:
var bytes = DownloadFile(absoluteUrl);
The problem looks to be double-reading - you are putting different things into the memory-stream / file-stream - it should be more like:
// declare file/memory stream here
while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
{
memoryStream.Write(buffer, 0, len);
fileStream.Write(buffer, 0, len);
// if you need to process "len" bytes, do it here
}
You might be able to lose "memoryStream" completely if you are processing the "len" bytes immediately. If it fits in-memory, it may be easier to just use WebClient.DownloadData and then File.WriteAllBytes.

How to check whether my C# code is reading the file in asynchronous mode?

public void fileReader()
{
Stream stream = new FileStream(filename, FileMode.Open , FileAccess.Read , FileShare.None, 30, true);
byte[] Buffer = new byte[30];
while (stream.Read(Buffer, 0, 30) > 0)
{
label1.text=Encoding.UTF8.GetString(Buffer);
Thread.Sleep(1000);
}
stream.Dispose();
}
THIS IS MY C# CODE TO READ 30 BYTES OF DATA AT A TIME FROM A FILE. I've created the Stream with FileStream constructor having useAsync=true. Here stream.Read method is used.
Is this read operation working in ASYNCHRNOUS mode?
How to check this?
Another problem is that Encoding.UTF8.GetString(Buffer); gives white spaces as '□' character... y?
Is there any other way to make a 1 second delay other than Thread.Sleep(1000); ?
It doesn't since you are using the syncronous Readmethod
if you wish to read it asyncronous you would have to use the BeginRead method
Below is an example on the usage of BeginRead from MSDN
private void ReadStreamAsyncImpl(Stream stream)
{
chunk = new byte[chunkSize];
stream.BeginRead(chunk,
0,
chunkSize,
new AsyncCallback(BeginReadCallback),
stream);
}
private void BeginReadCallback(IAsyncResult ar)
{
Stream stream = ar.AsyncState as Stream;
int bytesRead = stream.EndRead(ar);
StreamContentsAsString += StreamEncoding.GetString(chunk, 0, bytesRead);
if (bytesRead < chunkSize) {
// Finished
isOperationInProgress = false;
stream.Close();
if (null != ReadStreamCompleted) {
ReadStreamCompleted(this, new EventArgs());
}
} else {
ReadStreamAsyncImpl(stream);
}
}
That said you should probably use a StreamReader to read the characters of the stream instead of converting them yourself.
if you are using .NET 4.5 you can use ReadAsync as below (again from [MSDN][2])
private async void Button_Click(object sender, RoutedEventArgs e)
{
UnicodeEncoding uniencoding = new UnicodeEncoding();
string filename = #"c:\Users\exampleuser\Documents\userinputlog.txt";
byte[] result;
using (FileStream SourceStream = File.Open(filename, FileMode.Open))
{
result = new byte[SourceStream.Length];
await SourceStream.ReadAsync(result, 0, (int)SourceStream.Length);
}
UserInput.Text = uniencoding.GetString(result);
}
Reading your file like that seems to be causing you a lot of problems without solving anything. I do not see the point of reading 30 bytes at a time and then decode the result. You might end up decoding the last character wrong.
I recommend that you do something like this:
using(StreamReader reader = new StreamReader(filename, Encoding.UTF8))
{
while(!reader.EndOfStream)
{
String result = await reader.ReadLineAsync();
label1.Text = result;
}
}
But maybe a BackgroundWorker is much easier to use, sending the line to the UI using the progress callback.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
using(StreamReader reader = new StreamReader(e.Argument as String, Encoding.UTF8))
{
while(!reader.EndOfStream)
{
String line = reader.ReadLine();
backgroundWorker.ReportProgress(0, line);
Thread.Sleep(1000);
if (backgroundWorker.CancellationPending)
return;
}
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.UserState as String;
}

form turn to not responding after downloading a file from server

Its a very weird thing.
I created a client and a server to upload and download files. When uploading I can upload a lot of files without a problem but when I download a file the client for turn to not responding and doesn't show MessageBox.show("Downloaded"); its the first tie to see this :D.
The code that make the problem when used :
private void button3_Click(object sender, EventArgs e)
{
try
{
String fileToDownload = filePathDownload.Text;
TcpClient clientSocket = new TcpClient(serverIPDownload.Text, 8880);
NetworkStream networkStream = clientSocket.GetStream();
ASCIIEncoding asci = new ASCIIEncoding();
byte[] b = asci.GetBytes(fileToDownload + "?");
byte[] bb = asci.GetBytes("Download?");
int thisRead = 0;
int blockSize = 1024;
Byte[] dataByte = new Byte[blockSize];
networkStream.Write(bb, 0, bb.Length);
networkStream.Flush();
networkStream.Write(b, 0, b.Length);
networkStream.Flush();
using (FileStream fileStream = new FileStream(
"C:/Users/Laptop/Documents/Downloads/" + fileToDownload,
FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
while (true)
{
thisRead = networkStream.Read(dataByte, 0, blockSize);
fileStream.Write(dataByte, 0, thisRead);
if (thisRead == 0) break;
}
MessageBox.Show("File Downloaded");
fileStream.Close();
}
}
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
Thanks. This maybe off topic but its the problem I faced.
Your code appears to be ok, so I suspect the problem is in the Download method you are reading from.
Also, I would personally move the loop termination (if (thisRead == 0) break;) before the fileStream.Write statement.
And for production code, I would add some sort of timeout limit so that you don't end up in an infinite loop.

Categories