I am trying to download file from a remote linux server to my local computer using SftpClient.
Here is my code to download the file
public MemoryStream DownloadFile2(string path)
{
var connectionInfo = _taskService.GetBioinformaticsServerConnection();
MemoryStream fileStream = new MemoryStream();
using (SftpClient client = new SftpClient(connectionInfo))
{
client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200);
client.Connect();
client.DownloadFile(path, fileStream);
fileStream.Seek(0, SeekOrigin.Begin);
var response = new MemoryStream(fileStream.GetBuffer());
return fileStream;
}
}
And here is the controller that called above method.
public FileResult DownloadFile(string fullPath, string fileName)
{
if (!string.IsNullOrEmpty(fileName))
{
fullPath = string.Concat(fullPath, "/", fileName);
}
var ms = _reportAPI.DownloadFile2(fullPath);
var ext = Path.GetExtension(fullPath);
if (ext == ".xlsx")
{
return File(ms, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
}
return File(ms, "application/octet-stream", fileName);
}
I have manage to do it for most of the files, however for certain large '.xlsx' extension files, when I tried to open it, for some reason, I received below error.
If I am on IISExpress, I still manage to open it after I clicked on 'Yes' button, but if I'm using the normal IIS, it failed to open the file after clicked on 'Yes' button.
For other type of files or smaller excel files, it works as expected.
Any idea how can I modified my code to solve this issue?
I was able to resolve this by modifying my code as below
public MemoryStream DownloadFile2(string path)
{
var connectionInfo = _taskService.GetBioinformaticsServerConnection();
MemoryStream fileStream = new MemoryStream();
byte[] fileBytes = null;
using (SftpClient client = new SftpClient(connectionInfo))
{
client.ConnectionInfo.Timeout = TimeSpan.FromSeconds(200);
client.Connect();
client.DownloadFile(path, fileStream);
fileBytes = fileStream.ToArray();
var response = new MemoryStream(fileBytes);
return response;
}
}
Related
private async Task DoDownloadFile(ChatMessageListRefDataModel chatMessage)
{
var status = await Permissions.RequestAsync<Permissions.StorageWrite>();
if(status == PermissionStatus.Granted)
{
await HttpRequestHelper.DownloadFile(chatMessage.FileUrl, chatMessage.FileName);
}
}
public static async Task DownloadFile(string url, string fileName) {
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), fileName);
using (var downloadStream = await client.GetStreamAsync(url))
{
using (var memoryStream = new MemoryStream())
{
await downloadStream.CopyToAsync(memoryStream);
using(FileStream file = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))
{
byte[] bytes = new byte[memoryStream.Length];
memoryStream.Read(bytes, 0, (int)memoryStream.Length);
file.Write(bytes, 0, bytes.Length);
memoryStream.Close();
}
}
}
The code produces no error it is just the file was not found on the phone's directory. What could have gone wrong. Thanks.
I have created a new sample to test your code. And meet the same problem as yours. But the problem just appeared on the physical device. When I run it on the andorid emulator, I can find the file by the Android Studio's emulator device file explorer.
The path Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), fileName); seems be hidden. I can't find it by the device's file manager.
So you can try to use the following code to get the path, such as:
var filename1 = Android.App.Application.Context.GetExternalFilesDir("").AbsolutePath;
var filename = System.IO.Path.Combine(filename1, "xxx.txt");
using (System.IO.FileStream os = new System.IO.FileStream(filename, System.IO.FileMode.OpenOrCreate))
{
}
This file path can be found by the device's file manager. You can have a try.
Turns Out the Launcher.OpenAsync(fileUrl) is just okay to me.
I have a ftp server, where I have all the files stored. And it works fine with any ftp client. Now I have to download these file over HTTPS, I tried following approach but it is downloading the file in background and once download completes it asks for which location to save. It works fine if we have a small file, but when we have a large file, the browser keeps on loading till it download the file.
public ActionResult Download(string filePath)
{
string fileName = "file.csv.gz";
byte[] fileBytes = GetFile(#"\\myserver-ftp\f$\content\file.csv.gz");
return File(
fileBytes, "application/gzip", fileName);
}
byte[] GetFile(string s)
{
System.IO.FileStream fs = System.IO.File.OpenRead(s);
byte[] data = new byte[fs.Length];
int br = fs.Read(data, 0, data.Length);
if (br != fs.Length)
throw new System.IO.IOException(s);
return data;
}
Download FluentFtp nuget package into your project.
Create a method like this:
public async Task<FtpStatus> DownloadFtpFile(string ftpPathOfFile)
{
using (var client = new FtpClient(FtpHost))
{
client.Connect();
return client.DownloadFile(localPathToDownload, ftpPathOfFile);
}
}
Then you can call it asynchronously:
public ActionResult Download(string filePath)
{
string fileName = "file.csv.gz";
var fileFullPath = #"\\myserver-ftp\f$\content\file.csv.gz";
var ftpStatus = await DownloadFtpFile(fileFullPath);
if(ftpStatus== FtpStatus.Success)
{
return File(GetFile(fileFullPath), "application/gzip", fileName);
}
else
{
// return error message;
}
}
I want to store the zip file which is retrieved by the ToRetrieve method and store it on the local machine using SaveFileDialog controller in a Windows application writtein in C#.
This is my code:
if (!string.IsNullOrEmpty(FileName))
{
APIOrderMethods objAPIOrderMethods = new APIOrderMethods();
saveFileDialog1.Filter = "zip files (*.zip)|*.zip|All files (*.*)|*.*";
saveFileDialog1.Title = FileName;
if (saveFileDialog1.ShowDialog() == DialogResult.OK)
{
Stream stream = objAPIOrderMethods.ToRetrieve(FileName, ServicelURL, useName, password);
Stream streamToWriteTo = File.Open(saveFileDialog1.FileName, FileMode.Create, FileAccess.ReadWrite);
stream.CopyToAsync(streamToWriteTo);
}
label12.ForeColor = Color.Green;
}
But I get this error:
Exception :'Cannot access a closed Stream.'
ToRetrieve method for getting the Zip file:
public Stream ToRetrieve(string Filename, string serviceURL, string Username, string Password)
{
Stream Result = null;
var _saveDir = System.Configuration.ConfigurationManager.AppSettings["Save"];
try
{
using (HttpClient client = new HttpClient())
{
string url = "https://codeload.github.com/tugberkugurlu/ASPNETWebAPISamples/zip/master";
using (HttpResponseMessage response = client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead).Result)
using (Stream streamToReadFrom = response.Content.ReadAsStreamAsync().Result)
{
Result = streamToReadFrom;
}
}
}
catch (Exception ex)
{
throw;
}
return Result;
}
The problem lies in the method, ToRetrieve.
With streamToReadFrom being in a using statement, the Dispose method will be called.
Since result contains the reference, you are returning a closed stream.
I've set up a server endpoint that will zip a folder of files and return the zip file. On the client-side, I have code that calls the endpoint and saves the downloaded zip file to disk. All the code runs, but the resultant file is bigger than the zip file on the server and if I try to open the resultant zip file, I get "Windows can't open the file, file is invalid". What am I doing wrong?
Server code:
[Route("projects/files/download")]
[HttpPost]
public ActionResult Post([FromForm] DownloadFileRequest request)
{
string filesPath = ...;
string zipName = ...;
if (!Directory.Exists(filesPath)) {`
return BadRequest("File path not found on server");
}
if (System.IO.File.Exists(zipName)) System.IO.File.Delete(zipName);
System.IO.Compression.ZipFile.CreateFromDirectory(filesPath, zipName);
byte[] fileBytes = System.IO.File.ReadAllBytes(zipName);
FileContentResult zipFile = File(fileBytes, "application/zip", fileName);
return Ok(zipFile);
}
Client code:
Uri uri = new Uri("https://.../projects/files/download");
response = client.PostAsync(uri.ToString(), formContent).Result;
if (response.IsSuccessStatusCode)`
{
using (HttpContent content = response.Content)
{
Stream stream = content.ReadAsStreamAsync().Result;
string path = ...;
stream.Seek(0, SeekOrigin.Begin);
using (Stream streamToWriteTo = File.Open(path, FileMode.Create))
{
stream.CopyTo(streamToWriteTo);
}
}
}
Instead of returning the Ok(zipFile), just return the file:
return File(fileBytes, "application/zip", fileName);
I'm setting up a file transfer through Renci SSH.NET library using SFTP and C#.
And I'm stuck on this problem.
Whenever I upload a file in my ASP .NET Core program it sends the file, but it sends it as an empty file with the same name.
public async Task<IActionResult> UploadFiles(List<IFormFile> files)
{
string host = "----";
string username = "----";
string password = "----";
string Name = "";
var Stream = new MemoryStream();
List<MemoryStream> stream = new List<MemoryStream>();
var connectionInfo = new Renci.SshNet.ConnectionInfo(host, username, new PasswordAuthenticationMethod(username, password));
var sftp = new SftpClient(connectionInfo);
sftp.Connect();
sftp.ChangeDirectory("DIRECTORY");
try
{
//Read the FileName and convert it to Byte array.
foreach (var formFile in files)
{
var memoryStream = new MemoryStream();
await formFile.CopyToAsync(memoryStream);
Name = formFile.FileName;
using (var uplfileStream = memoryStream)
{
sftp.UploadFile(uplfileStream, Name, null);
}
}
}
catch (WebException ex)
{
throw new Exception((ex.Response as FtpWebResponse).StatusDescription);
}
sftp.Disconnect();
return View("Inbox");
}
I expect an output where a file is uploaded to the server, but the actual output is a file being uploaded to the server with the same name as the file I tried to upload, but the size is 0kb aka its empty.
Your immediate problem is answered here:
Upload from ByteArray/MemoryStream using SSH.NET - File gets created with size 0KB
Though you do not need the intermediate MemoryStream (and it's inefficient anyway).
Use IFormFile.OpenReadStream:
using (var uplfileStream = formFile.OpenReadStream())
{
sftp.UploadFile(uplfileStream, Name);
}