C# BinaryReader "stream does not support seek operations" - c#

I am trying to download files from an ftp server using C# and ftpwebrequest. I can get the bytes using BinaryReader, but when I try to read the stream using br.ReadBytes(int), I get an error that BinaryReader does not support seek operations.
Does anyone know how best to read the bytes so I can write them to a file?
Here's the full method:
public void DownloadFile(String fileName)
{
Logger.Info("starting to download file: " + fileName);
try
{
var downloadFileRequest = (FtpWebRequest)WebRequest.Create(FtpServer + "//" + fileName);
downloadFileRequest.Credentials = new NetworkCredential(FtpUsername,FtpPassword);
downloadFileRequest.Method = WebRequestMethods.Ftp.DownloadFile;
downloadFileRequest.UseBinary = true;
ServicePoint sp = downloadFileRequest.ServicePoint;
sp.ConnectionLimit = 2;
Logger.Info("getting ftp response for download file for " + fileName);
try
{
var downloadResponse = (FtpWebResponse)downloadFileRequest.GetResponse();
Logger.Info("getting ftp response stream for " + fileName);
try
{
Stream downloadStream = downloadResponse.GetResponseStream();
Logger.Info("File Download status: {0}", downloadResponse.StatusDescription.Trim());
Logger.Info("getting binary reader for " + fileName);
try
{
using ( var downloadReader = new BinaryReader(downloadStream))
{
String destinationFilePath= Path.Combine(LocalFolder, fileName);
Logger.Info("writing response stream to " + destinationFilePath);
try
{
using (var downloadWriter = new BinaryWriter(System.IO.File.Open(destinationFilePath, FileMode.Create)))
{
downloadWriter.Write(downloadReader.ReadBytes((int)downloadStream.Length));
}
Logger.Info("successfully saved " + destinationFilePath);
}
catch (Exception ex)
{
Logger.Info("could not save " + destinationFilePath+ " b/c: " + ex.Message);
}
}
}
catch (Exception ex)
{
Logger.Info("could not read " + fileName + " b/c: " + ex.Message);
}
}
catch (Exception ex)
{
Logger.Info("could not open download stream for " + fileName + " b/c: " + ex.Message);
}
finally
{
downloadResponse.Close();
}
}
catch (Exception ex)
{
Logger.Info("could not get ftp response stream for " + fileName + " b/c: " + ex.Message);
}
}
catch (Exception ex)
{
Logger.Info("could not get ftp request stream for " + fileName + " b/c: " + ex.Message);
}
}
This runs as part of an ongoing service, so I don't want to throw errors that would stop the service. Instead, I'm writing to a log. Here are the contents of the log for this method:
2009-10-07 16:33:29.1421|INFO|xxx.Web.Controllers.FtpController|starting to download file: 2009-10-06155728Z_metadata.txt
2009-10-07 16:33:29.1421|INFO|xxx.Web.Controllers.FtpController|getting ftp response for download file for 2009-10-06155728Z_metadata.txt
2009-10-07 16:33:29.6661|INFO|xxx.Web.Controllers.FtpController|getting ftp response stream for 2009-10-06155728Z_metadata.txt
2009-10-07 16:33:29.6661|INFO|xxx.Web.Controllers.FtpController|File Download status: 125 Data connection already open; Transfer starting.
2009-10-07 16:33:29.6721|INFO|xxx.Web.Controllers.FtpController|getting binary reader for 2009-10-06155728Z_metadata.txt
2009-10-07 16:33:29.6721|INFO|xxx.Web.Controllers.FtpController|writing response stream to C:\\Resumes\\2009-10-06155728Z_metadata.txt
2009-10-07 16:33:29.6951|INFO|xxx.Web.Controllers.FtpController|could not save C:\\Resumes\\2009-10-06155728Z_metadata.txt b/c: This stream does not support seek operations.
I've been working on this way too long, so any help would be appreciated!
Thanks!!

You shouldn't rely on Stream.Length, its possible it could be wrong. You just need to read all of the bytes in a while loop until there are no more bytes to read.
MemoryStream ms = new MemoryStream();
byte[] chunk = new byte[4096];
int bytesRead;
while ((bytesRead = downloadStream.Read(chunk, 0, chunk.Length)) > 0)
{
ms.Write(chunk, 0, bytesRead);
}
From there, all the read data is in the MemoryStream, and you can initialize the BinaryReader with that.

Darkassassin's answer works great! Here's the code I finally got to work before I saw his post. It's slightly different b/c it writes directly to the file rather than to the memory stream.
I replaced this line:
downloadWriter.Write(downloadReader.ReadBytes((int)downloadStream.Length));
with this:
var buffer = new byte[BufferSize];
int readCount = downloadStream.Read(buffer, 0, BufferSize);
while (readCount > 0)
{
downloadWriter.Write(buffer, 0, readCount);
readCount = downloadStream.Read(buffer, 0, BufferSize);
}
(BufferSize = 4096)
Thanks again for the quick help!!

Related

Change file permission after FTP Upload

I am uploading files in a folder on the desktop to the Windows Server 2012 server.
The upload is proceeding correctly, but I need to change the read and delete permission of the uploaded file.
How can I do this in this code?
string ftpIPServidor = "XXXX";
string ftpUsuarioID = "XX";
string ftpSenha = "XXXXXXX";
FileInfo _arquivoInfo = new FileInfo(_nomeArquivo);
string uri = "ftp://" + ftpIPServidor + "/" + _arquivoInfo.Name;
FtpWebRequest requisicaoFTP;
requisicaoFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpIPServidor + "/" + _arquivoInfo.Name));
requisicaoFTP.Credentials = new NetworkCredential(ftpUsuarioID, ftpSenha);
requisicaoFTP.KeepAlive = false;
requisicaoFTP.Method = WebRequestMethods.Ftp.UploadFile;
requisicaoFTP.UseBinary = true;
requisicaoFTP.ContentLength = _arquivoInfo.Length;
// Define o tamanho do buffer para 2kb
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int _tamanhoConteudo;
FileStream fs = _arquivoInfo.OpenRead();
var horaAgora = DateTime.Now;
try
{
Stream strm = requisicaoFTP.GetRequestStream();
_tamanhoConteudo = fs.Read(buff, 0, buffLength);
while (_tamanhoConteudo != 0)
{
// Escreve o conteudo a partir do arquivo para o stream FTP
strm.Write(buff, 0, _tamanhoConteudo);
_tamanhoConteudo = fs.Read(buff, 0, buffLength);
}
strm.Close();
fs.Close();
Console.WriteLine(horaAgora + " :> Upload of " + _arquivoInfo.Name);
fi.Delete();
}
catch (Exception ex)
{
Console.WriteLine(horaAgora + " :> Err " + _arquivoInfo.Name);
}
There are extension methods called GetAccessControl and SetAccessControl in the package System.IO.FileSystem.AccessControl
More info here
How to modify file access control in .NET Core

FtpWebRequest - TransferSpeed - Connection forcibly closed

I am upload large tar files about 10GB to the ftp server. It is taking a lot of time and throws the following error: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
I have referred to the following:
C# : FTP upload buffer size
Why is my ftp upload method so slow?
https://www.codeproject.com/Questions/204070/how-to-write-c-code-to-increase-the-ftp-file-uploa
The following is the code:
FileStream fs = null;
Stream rs = null;
try
{
string file = args[0].Replace("---"," ");
string ftpServer = args[1].ToString();
string uploadFileName = new FileInfo(file).Name;
string uploadUrl = ftpServer;
Console.WriteLine("Start Time: {0}", DateTime.Now);
Console.WriteLine("File Name: {0}",file);
fs = new FileStream(file, FileMode.Open, FileAccess.Read);
string ftpUrl = string.Format("{0}/{1}", uploadUrl, uploadFileName);
FtpWebRequest requestObj = FtpWebRequest.Create(ftpUrl) as FtpWebRequest;
requestObj.Timeout = -1; // <---- -1 is Infinite
requestObj.Method = WebRequestMethods.Ftp.UploadFile;
rs = requestObj.GetRequestStream();
byte[] buffer = new byte[4096];
//byte[] buffer = new byte[16000]; // <--- 16k
int read = 0;
while ((read = fs.Read(buffer, 0, buffer.Length)) != 0)
{
rs.Write(buffer, 0, read);
}
rs.Flush();
}
catch (Exception ex)
{
Console.WriteLine("File upload/transfer Failed.\r\nError Message:\r\n" + ex.Message);
}
finally
{
if (fs != null)
{
fs.Close();
fs.Dispose();
}
if (rs != null)
{
rs.Close();
rs.Dispose();
}
}
Console.WriteLine("End Time: {0}", DateTime.Now);
Console.WriteLine("Exiting the application.. press any key to continue");
Console.ReadLine();
UPDATE: I am uploading the file to FTP DropBox and No access to the logs. While trying to upload via clients like FileZilla the speed is faster. The limit of the dropbox is 300 GB. Is it possible to find the rate of transfer?
Please explain the solution as well for my understanding as I am a beginner.
Thanks in advance.

Transferring files through sockets (bir and small sized)

Hello I'm trying to implement a server and client couple that can transfer files of any kind and any size but there is a problem I somehow corrupt files. I tried lots of methods but can't figure it out. So basicly I can connect ,I can understand which file does client want, and can send it through sockets. When I try to open that file it shows me an error message(Tried winrar, mp4, avi files) Here is my code:
//Server
private void Receive(string receivedFileName, string fileSize)
{
try
{
int receivedBytesLen = 0;
byte[] incomingFile = new byte[int.Parse(fileSize)];
activity.AppendText("Preparing to download... \n");
while (incomingFile != null && int.Parse(fileSize) > receivedBytesLen)
{
tempSocket.Receive(incomingFile);
receivedBytesLen = incomingFile.Length;
int fileNameLen = BitConverter.ToInt32(incomingFile, 0);
File.WriteAllBytes(fileDir + "//" + receivedFileName, incomingFile);
}
activity.AppendText("File saved to " + fileDir + "\n");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//////////////////////////////////////////////////////////////////////
//server option 2
private void Receive(string receivedFileName, string fileSize)
{
try
{
byte[] incomingFile = new byte[10124 * 5000];
activity.AppendText("Preparing to download... \n");
BinaryWriter bWrite = new BinaryWriter(File.Open(folderBrowserDialog1.SelectedPath + "//" + receivedFileName, FileMode.Append));
int receivedBytesLen = tempSocket.Receive(incomingFile, incomingFile.Length, 0);
int fileNameLen = BitConverter.ToInt32(incomingFile, 0);
//string fileName = Encoding.UTF8.GetString(incomingFile, 4, fileNameLen);
//bWrite.Write(incomingFile, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);
while (receivedBytesLen > 0)
{
receivedBytesLen = tempSocket.Receive(incomingFile, incomingFile.Length, 0);
if (receivedBytesLen == 0)
{
bWrite.Close();
}
else
{
bWrite.Write(incomingFile, 0, receivedBytesLen);
}
}
activity.AppendText("File saved to " + fileDir + "\n");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
/////////////////////////////////////////////////////////////////////
//client
private void Upload_Click(object sender, EventArgs e)
{//Uploads selected file after clicking upload button.
try
{
if (clientSocket.Connected)
{
byte[] buffer = new byte[1024];
byte[] sendingFile = null;
sendingFile = File.ReadAllBytes(openFileDialog1.FileName);
FileInfo f = new FileInfo(openFileDialog1.FileName);
string fileSize = f.Length.ToString();
buffer = Encoding.Default.GetBytes(getUserName.Text + "Upload" + openFileDialog1.SafeFileName + "size" + fileSize + "end");
clientSocket.Send(buffer);
activityLog.AppendText("Sending File \n");
int bytesToBeSent = sendingFile.Length;
int bytesActuallySent = 0;
while(bytesActuallySent < bytesToBeSent){
bytesActuallySent += clientSocket.Send(sendingFile, bytesActuallySent, bytesToBeSent -bytesActuallySent, 0);
}
activityLog.AppendText("File Sent.\n");
}
}
catch(Exception ex){
MessageBox.Show(ex.Message);
}
}
server option #2 is better. But there are some improvements
socket receive buffer is limited decided by OS, generally is 8192
use FileStream in client and server and do not forget to close filestream after file download
client:
FileStream fileStream = new FileStream(filePath, FileMode.Open)
byte[] buff = new byte[8192];
do
{
bytesRead = fileStream.Read(buff, 0, buff.Length);
sock.send(dataSock, buff, bytesRead);
}while (bytesRead > 0);
fileStream.close();
server:
FileStream fileStream = new FileStream(filePath, FileMode.Open)
do
{
bytesRead = sock.receive(buff, 0, buff.Lenght);
fileStream.Write(buff, 0, bytesRead );
}while (bytesRead > 0);
fileStream.close();
In order to satisfy ANY sized file you need an option #3, one that reads a block of bytes til all bytes are received, your option #1 wouldn't do so well on a 3 GIG file running on 2 GIG box...
Here is a link: http://www.yoda.arachsys.com/csharp/readbinary.html
I like option #2 from this link.
However in the example they write to a memoryStream, you should write to a filestream, the target file.

C# - Download Directory - FTP

I'm trying to download a directory, using FTP in a C# application. I basically need to take a remote dir, and move it, and its contents into a local dir.
Here is the function I'm currently using, and what the log output and errors are. The sample I'm referencing is for getting files, and possibly not directories:
private void Download(string file, string destination)
{
try
{
string getDir = "ftp://" + ftpServerIP + ftpPath + file + "/";
string putDir = destination + "\\" + file;
Debug.WriteLine("GET: " + getDir);
Debug.WriteLine("PUT: " + putDir);
FtpWebRequest reqFTP;
reqFTP = (FtpWebRequest)FtpWebRequest.Create
(new Uri(getDir));
reqFTP.Credentials = new NetworkCredential(ftpUserID,
ftpPassword);
reqFTP.UseBinary = true;
reqFTP.KeepAlive = false;
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.Proxy = null;
reqFTP.UsePassive = false;
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream responseStream = response.GetResponseStream();
FileStream writeStream = new FileStream(putDir, FileMode.Create);
int Length = 2048;
Byte[] buffer = new Byte[Length];
int bytesRead = responseStream.Read(buffer, 0, Length);
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = responseStream.Read(buffer, 0, Length);
}
writeStream.Close();
response.Close();
}
catch (WebException wEx)
{
MessageBox.Show(wEx.Message, "Download Error");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Download Error");
}
}
Debug:
GET: ftp://I.P.ADDR/SOME_DIR.com/members/forms/THE_FOLDER_TO_GET/
PUT: C:\Users\Public\Documents\iMacros\Macros\THE_FOLDER_TO_WRITE
A first chance exception of type 'System.Net.WebException' occurred in System.dll
MessageBox Output:
The requested URI is invalid for this FTP command.
The slash on the end of the getDir indicates a directory - can you use mget and pass a path like that ends in "/*"?

c# upload file to stream?

I want the user to upload a file and save it to a stream.
Here is the code so far:
private void Submit_ServerClick(object sender, System.EventArgs e)
{
fn = System.IO.Path.GetFileName(File1.PostedFile.FileName);
}
you could do like this
string filePath = uploadFile(fileUploadControl.FileContent);
private string uploadFile(Stream serverFileStream)
{
string filename = ConfigurationManager.AppSettings["FileUploadTempDir"] +
DateTime.Now.ToString("yyyyMMddhhmm") + "_" +
Customer.GetCustomerName(CustomerId).Replace(" ", "_") + ".txt";
try
{
int length = 256;
int bytesRead = 0;
Byte[] buffer = new Byte[length];
// write the required bytes
using (FileStream fs = new FileStream(filename, FileMode.Create))
{
do
{
bytesRead = serverFileStream.Read(buffer, 0, length);
fs.Write(buffer, 0, bytesRead);
}
while (bytesRead == length);
}
serverFileStream.Dispose();
return filename;
}
catch (Exception ex)
{
lblErrorMessage.Text += "An unexpeded error occured uploading the file. " + ex.Message;
return string.Empty;
}
}
i hope it will helps you...
The object that FileUpload.PostedFile returns has an InputStream property you can read the uploaded file data from.
Looks like this one http://support.microsoft.com/kb/323246
string fn = System.IO.Path.GetFileName(File1.PostedFile.FileName);
string SaveLocation = Server.MapPath("Data") + "\\" + fn;
try
{
File1.PostedFile.SaveAs(SaveLocation);
Response.Write("The file has been uploaded.");
}
catch ( Exception ex )
{
Response.Write("Error: " + ex.Message);
//Note: Exception.Message returns a detailed message that describes the current exception.
//For security reasons, we do not recommend that you return Exception.Message to end users in
//production environments. It would be better to put a generic error message.
}

Categories