I have a requirement to pass a file through an MVC action method.
To download it from a Web API method and return it as a result.
The code I have is assembled from a few answers here on SO and some other references.
The problem is that the file seems to be locked by the download process when I try to return it as a result. I thought that the tsk.Wait() wold solve the problem.
Perhaps someone knows of a better solution?
using (var client = HttpClientProvider.GetHttpClient())
{
client.BaseAddress = new Uri(baseAddress);
await client.GetAsync("api/Documents/" + fileName).ContinueWith(
(requestTask) =>
{
HttpResponseMessage response = requestTask.Result;
response.EnsureSuccessStatusCode();
fileName = response.Content.Headers.ContentDisposition.FileName;
if (fileName.StartsWith("\"") && fileName.EndsWith("\""))
{
fileName = fileName.Trim('"');
}
if (fileName.Contains(#"/") || fileName.Contains(#"\"))
{
fileName = Path.GetFileName(fileName);
}
path = Path.Combine(GetDocsMapPath(), fileName);
System.Threading.Tasks.Task tsk = response.Content.ReadAsFileAsync(path, true).ContinueWith(
(readTask) =>
{
Process process = new Process();
process.StartInfo.FileName = path;
process.Start();
});
tsk.Wait();
HttpResponseMessage resp = new HttpResponseMessage(HttpStatusCode.OK);
resp.Content = new StreamContent(new FileStream(path, FileMode.Open, FileAccess.Read));
resp.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment");
resp.Content.Headers.ContentDisposition.FileName = fileName;
return resp;
});
}
public static Task ReadAsFileAsync(this HttpContent content, string filename, bool overwrite)
{
string pathname = Path.GetFullPath(filename);
if (!overwrite && File.Exists(filename))
{
throw new InvalidOperationException(string.Format("File {0} already exists.", pathname));
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(pathname, FileMode.Create, FileAccess.Write, FileShare.None);
return content.CopyToAsync(fileStream).ContinueWith(
(copyTask) =>
{
fileStream.Close();
fileStream.Dispose();
});
}
catch
{
if (fileStream != null)
{
fileStream.Close();
fileStream.Dispose();
}
throw;
}
}
first, I don't see any useful function achieved by the process you launch, apart from locking your file, which you don't want.
try removing these lines and retrying.
.ContinueWith(
(readTask) =>
{
Process process = new Process();
process.StartInfo.FileName = path;
process.Start();
});
Edit: Using FilePathResult
I don't know about your exact requirements, but if your goal is to return a file that you have the path for; then the easiest is to return a FilePathResult which will take care of reading and returning the contents of the file to the requester.
public FilePathResult GetFile()
{
//put your logic to determine the file path here
string name = ComputeFilePath();
//verify that the file actually exists and retur dummy content otherwise
FileInfo info = new FileInfo(name);
if (!info.Exists)
{
using (StreamWriter writer = info.CreateText())
{
writer.WriteLine("File Not Found");
}
}
return File(name, "application/octet-stream");
}
if you are sure of what type your content is , change the mime type accordingly, otherwise it's better to leave it as a binary data.
Related
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;
}
}
I am trying to write some json text. But I get an Exception like
The process cannot access the file C:\blah blah\SystemInActivity.json because it is being used by an other process. But then second time when I run the app after json file is created and then when I write I dont get an exception. Please help.
class ApplicationSettingsViewModel
{
ApplicationSettingsModel model;
MemoryMappedFile mmf = null;
public string FullPath = string.Empty;
//This is not a singleton class but I guess it has to be one but its ok for demonstration.
public ApplicationSettingsViewModel()
{
model = new ApplicationSettingsModel();
CreateFileWithoutMemoryMap();
//MemoryMapped();
}
public string GetDriectory()
{
return Path.GetDirectoryName(FullPath);
}
private void CreateFileWithoutMemoryMap()
{
var info = Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) + "/" + model.Data.Settings.OrcaUISpecificSettings.TimeOutFolder);
string path = Path.Combine(info.FullName + #"\" + model.Data.Settings.OrcaUISpecificSettings.File);
//mmf = MemoryMappedFile.CreateFromFile(path, FileMode.CreateNew, "MyMemoryFile", 1024 * 1024, MemoryMappedFileAccess.ReadWrite);
FullPath = path;
if (!File.Exists(path))
{
File.Create(path);
}
}
public void WriteToFile(string json)
{
try
{
FileStream fileStream = File.Open(FullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); //This line giving Exception
fileStream.SetLength(0);
fileStream.Close(); // This flushes the content, too.
using (StreamWriter sw = new StreamWriter(FullPath))
{
sw.Write(json);
}
}
catch (Exception ex)
{
}
}
In the constructor of the MainWindow I am calling the write method
private ApplicationSettingsViewModel AppViewModel;
public MainWindow()
{
InitializeComponent();
//MessageBox.Show("App Started");
AppViewModel = new ApplicationSettingsViewModel();
WriteToFile("Active");
}
public void WriteToFile(string status)
{
var root = new Root();
string jsonString = string.Empty;
root.AllApplications.Add(new DataToWrite() { AppName = "DevOrca", Status = status });
try
{
jsonString = JsonConvert.SerializeObject(root, Formatting.Indented);
}
catch (Exception ex)
{
MessageBox.Show(jsonString);
MessageBox.Show("Exception");
}
mutex.WaitOne();
//Serialize Contents and write
AppViewModel.WriteToFile(jsonString);
//var access = AppViewModel.GetAccessor();
//byte[] bytes = Encoding.ASCII.GetBytes(jsonString);
//access.Write(bytes, 0, bytes.Length);
mutex.ReleaseMutex();
}
File.Create() method opens FileStream to create a file and you need to close it, something like this:
File.Create(path).Close();
I'm working on a website where users could apply to a certain job online.
A user must submit information in addition to a CV.
I'm new to this kind of work I would appreciate any kind of help.
Here is my attempt for the post method, but it generates the following exception:
Unexpected end of stream. Is there an end boundary?
public async Task<IHttpActionResult> PostCV()
{
IList<string> AllowedFileExtensions = new List<string> { ".txt", ".pdf" };
var parser = new MultipartFormDataParser(await Request.Content.ReadAsStreamAsync());
IList<FilePart> files = parser.Files;
IList<ParameterPart> formData = parser.Parameters;
FilePart uploadedContent = files.First();
var originalContentFileName =
uploadedContent.FileName.Trim('\"');
var originalExtension = Path.GetExtension(originalContentFileName);
if (!AllowedFileExtensions.Contains(originalExtension))
return BadRequest("Bad extension");
string modifiedContentFileName =
string.Format("{0}{1}", Guid.NewGuid().ToString(),
originalExtension);
Stream input = uploadedContent.Data;
string Url = string.Empty;
string fileName = string.Empty;
string directoryName = string.Empty;
directoryName = Path.Combine(HttpRuntime.AppDomainAppPath, "Uploads");
fileName = Path.Combine(directoryName, modifiedContentFileName);
if (File.Exists(fileName))
File.Delete(fileName);
using (Stream file = File.OpenWrite(fileName))
{
try
{
input.CopyTo(file);
file.Close();
var cv = new CV{ Path = fileName };
db.CVs.Add(cv);
db.SaveChanges();
return Json(cv);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
}
Thanks in advance
Apparently I should have chosen form-data in the body section, set the key with anything (e.g filename) and in the value choose file.
Also you need to create a folder in your path, in my case called Uploads in the directory specified.
Lots of thanks for those who helped.
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]);
}