Get FileStream from form posted file - asp.net

I have a control on view page. When user selects the file and clicks on submit button this makes ajax call to upload the file on server. Unfortunately my server method accepts file path (like C:/Videos/1.mp4) to upload. This works great with string demoPath in the code below but I'm not sure how to get similar path when user selects in control. Due to sercurity reasons modern browsers not allows exposing paths. How to achieve this?
[HttpPost]
public async Task<JsonResult> Upload(string lectureId, string filepath)
{
for (int i = 0; i < Request.Files.Count; i++)
{
//// This works great
//string demoPath = "C:/Users/abchi/Desktop/BigBuckBunny.mp4";
var file = Request.Files[i];
var fileName = Path.GetFileName(file.FileName);
//var path = Path.Combine(Server.MapPath("~/User/"), fileName);
//file.SaveAs(path);
//await RunUploader(demoPath);
await RunUploader(get_path_from_posted_file_or_request);
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task RunUploader(string filePath)
{
// :::::::
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
// ::::
}
// ::::::
}

I'm not sure this is expected because I did not quite understand.
Download the file path of the user's computer can not be - https://stackoverflow.com/a/15201258/4599089
but if you want to have access to the FileStream on your server:
File has InputStream and you can use this:
[HttpPost]
public async Task<JsonResult> Upload(string lectureId, string filepath)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/User/"), fileName);
var fileStream = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
file.InputStream.CopyTo(fileStream);
fileStream.Close();
await RunUploader(path); //path or stream
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task RunUploader(string filePath)
{
// :::::::
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
// ::::
}
// ::::::
}

I asked my fellow dev to make necessary changes in public async Task RunUploader(string filePath) parameters. Said code was part of YouTube .NET samples for console apps. Now we are developing for web, in this case we can't pass full path. So they made following changes:
[HttpPost]
public async Task<JsonResult> Upload(string lectureId)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
Stream fileStream = file.InputStream;
await Run(fileStream);
}
return Json(new { error = false, message = "Video uploaded." });
}
public async Task Run(Stream fileStream)
{
// ::::::::::
using (fileStream)
{
// ::::::
}
// ::::::::::
}
Now with this change everything started working.

Related

ASP.NET core with MVC folder pathing/mapping FAIL

Whenever I upload a file I want to have it automatically converted into .pdf (I am doing that using NuGet). The thing is the upload scheme is done using relative paths. I do not know what to put into these parentheses:
var wordDocument = appWord.Documents.Open(uploadedFile);
What should I replace uploadedFile with in order to work? I will leave my relative path mapping code below:
public IActionResult Index1()
{
// Get files from the server
var model = new FilesViewModel();
foreach (var item in Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "upload")))
{
model.Files.Add(
new FileDetails { Name = System.IO.Path.GetFileName(item), Path = item });
}
return View(model);
}
[HttpPost]
public IActionResult Index1(IFormFile[] files)
{
// Iterate each files
foreach (var file in files)
{
// Get the file name from the browser
var fileName = System.IO.Path.GetFileName(file.FileName);
// Get file path to be uploaded
var filePath = Path.Combine(Directory.GetCurrentDirectory(), "upload", fileName);
// Check If file with same name exists and delete it
if (System.IO.File.Exists(filePath))
{
System.IO.File.Delete(filePath);
}
// Create a new local file and copy contents of uploaded file
using (var localFile = System.IO.File.OpenWrite(filePath))
using (var uploadedFile = file.OpenReadStream())
{
var appWord = new Application();
if (appWord.Documents != null)
{
//yourDoc is your word document
var wordDocument = appWord.Documents.Open(uploadedFile);
string pdfDocName = "pdfDocument.pdf";
if (wordDocument != null)
{
wordDocument.ExportAsFixedFormat(pdfDocName,
WdExportFormat.wdExportFormatPDF);
wordDocument.Close();
}
appWord.Quit();
}
uploadedFile.CopyTo(localFile);
}
}
ViewBag.Message = "Files are successfully uploaded";
// Get files from the server
var model = new FilesViewModel();
foreach (var item in Directory.GetFiles(Path.Combine(Directory.GetCurrentDirectory(), "upload")))
{
model.Files.Add(
new FileDetails { Name = System.IO.Path.GetFileName(item), Path = item });
}
return View(model);
}
public async Task<IActionResult> Download(string filename)
{
if (filename == null)
return Content("filename is not availble");
var path = Path.Combine(Directory.GetCurrentDirectory(), "upload", filename);
var memory = new MemoryStream();
using (var stream = new FileStream(path, FileMode.Open))
{
await stream.CopyToAsync(memory);
}
memory.Position = 0;
return File(memory, GetContentType(path), Path.GetFileName(path));
}
private string GetContentType(string path)
{
var types = GetMimeTypes();
var ext = Path.GetExtension(path).ToLowerInvariant();
return types[ext];
}
private Dictionary<string, string> GetMimeTypes()
{
return new Dictionary<string, string>
{
{".txt", "text/plain"},
{".pdf", "application/pdf"},
{".doc", "application/vnd.ms-word"},
{".docx", "application/vnd.ms-word"},
{".xls", "application/vnd.ms-excel"},
{".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
{".png", "image/png"},
{".jpg", "image/jpeg"},
{".jpeg", "image/jpeg"},
{".gif", "image/gif"},
{".csv", "text/csv"}
};
}

Return a file from asp.net web api

I've been trying to return a file from my web api. Below is my code somehow downloads the file, but the downloaded file is corrupted.
SomeMethod
{
var stream = new MemoryStream();
// processing the stream.
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(response.FileArray.ToArray())
};
result.Content.Headers.ContentDisposition =
new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = "CertificationCard.pdf"
};
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");
return result;
}
public HttpResponseMessage Download([FromUri] DownloadRequest req) { }

How to add property "Alternate" to an image file on server?

I'm using asp.net core and web api for uploading images.
On server:
[Produces("application/json")]
[Route("api/Upload")]
public class UploadApiController : Controller
{
private readonly IHostingEnvironment _environment;
public UploadApiController(IHostingEnvironment environment)
{
_environment = environment;
}
[HttpPost]
public async Task Post(ICollection<IFormFile> files)
{
//...
}
}
On client:
// Files is an array that contains all temporary images for uploading.
let Files = [];
let image_preview = function (file) {
file['Alternate'] = 'alternate_text';
Files.push(file);
// other implements...
};
$('button#upload').click(function () {
let formData = new FormData();
for (let i = 0; i < Files.length; i++) {
formData.append('files', Files[i])
}
let xhr = new XMLHttpRequest();
xhr.open('POST', '/api/upload', true);
xhr.onload = function () {
console.log('uploading...')
};
xhr.send(formData);
});
Snapshot:
My question: how to add new property "Alternate" to ICollection<IFormFile> files to detect property Alternate that is sent from client (formData)?
It's not the answer for question How to add property “Alternate” to an image file on server? but it seems like solving the problem (sending image file with alternate text).
On server:
using Newtonsoft.Json;
[HttpPost]
public async Task Post(ICollection<IFormFile> files, IList<string> alts)
{
IDictionary<string, string> _alts = new Dictionary<string, string>();
foreach (var alt in alts)
{
IDictionary<string, string> temp = JsonConvert.DeserializeObject<Dictionary<string, string>>(alt);
foreach (var item in temp)
{
_alts.Add(item.Key, item.Value);
}
}
}
On client:
for (let i = 0; i < Files.length; i++) {
formData.append('files', Files[i]);
let name = Files[i]['name'],
alt = {};
alt[name] = 'alt_text';
formData.append('alts', JSON.stringify(alt));
}
We would never get duplicate key in the dictionary because Files[i]['name'] is always primary and cannot be changed (hacking by someone) if we've checked duplicate uploading file before.
Then, we can merge the file name (in files) with Key in _alts to get Alternate text.
Snapshot:
UPDATE: The code in the snapshot was wrong.

Windows Store App HttpClient progress

I need to provide the download progress inside the app for an arbitrary file.
I thought I could use HttpClient.GetInputStreamAsync progress but it is not fine grained. The progress notification is delivered very rare. The code below:
var httpClient = new HttpClient();
var path = new Uri(#"http://<path>");
using (var inputStream = (await httpClient.GetInputStreamAsync(path).AsTask(new MyProgress())).AsStreamForRead())
{
var outputFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("output.mov", CreationCollisionOption.ReplaceExisting);
using (var outputStream = (await outputFile.OpenAsync(FileAccessMode.ReadWrite)).AsStreamForWrite())
{
await inputStream.CopyToAsync(outputStream);
await outputStream.FlushAsync();
}
}
Debug.WriteLine("done");
private class MyProgress : IProgress<HttpProgress>
{
public void Report(HttpProgress value)
{
Debug.WriteLine("HTTP {0} progress {1}/{2}", value.Stage, value.BytesReceived, value.TotalBytesToReceive);
}
}
outputs:
HTTP ResolvingName progress 0/
HTTP ConnectingToServer progress 0/
HTTP SendingHeaders progress 0/
HTTP WaitingForResponse progress 0/
HTTP ReceivingHeaders progress 0/
HTTP ReceivingContent progress 0/109192147
done
So at some point of time the total bytes to receive is known but no progress reported during file download.
I ended up with solution like below:
var httpClient = new HttpClient();
var path = new Uri(#"http://<path>");
var progressHelper = new ProgressHelper();
using (var inputStream = (await httpClient.GetInputStreamAsync(path).AsTask(progressHelper)).AsStreamForRead())
{
var outputFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("output.mov", CreationCollisionOption.ReplaceExisting);
using (var outputStream = (await outputFile.OpenAsync(FileAccessMode.ReadWrite)).AsStreamForWrite())
{
var buffer = new byte[1024 * 1024];
int read;
while ((read = await inputStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
progressHelper.BytesReceived += (ulong)read;
await outputStream.WriteAsync(buffer, 0, read);
}
await outputStream.FlushAsync();
}
}
Debug.WriteLine("done");
private class ProgressHelper : IProgress<HttpProgress>
{
private ulong _bytesReceived = 0;
private ulong? _totalBytesToReceive = null;
public void Report(HttpProgress value)
{
Debug.WriteLine("HTTP {0} progress {1}/{2}", value.Stage, value.BytesReceived, value.TotalBytesToReceive);
TotalBytesToReceive = value.TotalBytesToReceive;
}
public ulong? TotalBytesToReceive
{
get
{
return _totalBytesToReceive;
}
set
{
if (_totalBytesToReceive == value)
return;
_totalBytesToReceive = value;
UpdateProgress();
}
}
public ulong BytesReceived
{
get
{
return _bytesReceived;
}
set
{
if (_bytesReceived == value)
return;
_bytesReceived = value;
UpdateProgress();
}
}
public int? Progress
{
get
{
if (_totalBytesToReceive.HasValue)
{
return (int)Math.Round((100.0 * _bytesReceived) / _totalBytesToReceive.Value);
}
return null;
}
}
private void UpdateProgress()
{
Debug.WriteLine("Progress: {0}", Progress);
}
}
Since the HttpClient.GetInputStreamAsync doesn't support progress during file download, you can use other methods of HttpClient such as GetAsync andGetBufferAsync.The following code is updating of your code to use HttpClient.GetAsync.
var httpClient = new HttpClient();
var path = new Uri("https://sec.ch9.ms/slides/developerGuideToWindows10/02-XAMLcontrols.pptx");
HttpResponseMessage response = await httpClient.GetAsync(path).AsTask(new MyProgress());
using (Stream responseStream = (await response.Content.ReadAsInputStreamAsync()).AsStreamForRead())
{
var outputFile = await ApplicationData.Current.TemporaryFolder.CreateFileAsync("output.pptx", CreationCollisionOption.ReplaceExisting);
using (var outputStream = (await outputFile.OpenAsync(FileAccessMode.ReadWrite)).AsStreamForWrite())
{
await responseStream.CopyToAsync(outputStream);
await outputStream.FlushAsync();
}
}
Debug.WriteLine("done");
And the outputs

MVC3 Valums Ajax File Upload

I'm trying to use valums ajax uploader. http://valums.com/ajax-upload/
I have the following on my page:
var button = $('#fileUpload')[0];
var uploader = new qq.FileUploader({
element: button,
allowedExtensions: ['jpg', 'jpeg', 'png', 'gif'],
sizeLimit: 2147483647, // max size
action: '/Admin/Home/Upload',
multiple: false
});
it does post to my controller but qqfile is always null. I tried these:
public ActionResult Upload(HttpPostedFile qqfile)
AND
HttpPostedFileBase file = Request.Files["file"];
without any luck.
I found an example for ruby on rails but not sure how to implement it in MVC
http://www.jigsawboys.com/2010/10/06/ruby-on-rails-ajax-file-upload-with-valum/
In firebug i see this:
http://localhost:61143/Admin/Home/Upload?qqfile=2glonglonglongname+-+Copy.gif
I figured it out. this works in IE and Mozilla.
[HttpPost]
public ActionResult FileUpload(string qqfile)
{
var path = #"C:\\Temp\\100\\";
var file = string.Empty;
try
{
var stream = Request.InputStream;
if (String.IsNullOrEmpty(Request["qqfile"]))
{
// IE
HttpPostedFileBase postedFile = Request.Files[0];
stream = postedFile.InputStream;
file = Path.Combine(path, System.IO.Path.GetFileName(Request.Files[0].FileName));
}
else
{
//Webkit, Mozilla
file = Path.Combine(path, qqfile);
}
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
System.IO.File.WriteAllBytes(file, buffer);
}
catch (Exception ex)
{
return Json(new { success = false, message = ex.Message }, "application/json");
}
return Json(new { success = true }, "text/html");
}
This component is sending an application/octet-stream instead of multipart/form-data which is what the default model binder can work with. So you cannot expect Request.Files to have any value with such a request.
You will need to manually read the request stream:
public ActionResult Upload(string qqfile)
{
var stream = Request.InputStream;
var buffer = new byte[stream.Length];
stream.Read(buffer, 0, buffer.Length);
var path = Server.MapPath("~/App_Data");
var file = Path.Combine(path, qqfile);
File.WriteAllBytes(file, buffer);
// TODO: Return whatever the upload control expects as response
}
IE uploads using multipart-mime. Other browsers use Octet-Stream.
I wrote an upload handler to work with Valums Ajax Uploader that works with both MVC & Webforms & both upload methods. I'd be happy to share with you if you wanted. It closely follows the the PHP handler.
My controller to handle the upload looks like this:
public class UploadController : Controller
{
private IUploadService _Service;
public UploadController()
: this(null)
{
}
public UploadController(IUploadService service)
{
_Service = service ?? new UploadService();
}
public ActionResult File()
{
return Content(_Service.Upload().ToString());
}
The UploadService looks this:
public class UploadService : IUploadService
{
private readonly qq.FileUploader _Uploader;
public UploadService()
: this(null)
{ }
public UploadService(IAccountService accountservice)
{
_Uploader = new qq.FileUploader();
}
public UploadResult Upload()
{
qq.UploadResult result = _Uploader.HandleUpload();
if (!result.Success)
return new UploadResult(result.Error);
.... code .....
return new UploadResult((Guid)cmd.Parameters["#id"].Value);
}
catch (Exception ex)
{
return new UploadResult(System.Web.HttpUtility.HtmlEncode(ex.Message));
}
finally
{
............code.........
}
}
...............code ............
You should try:
Stream inputStream = (context.Request.Files.Count > 0) ? context.Request.Files[0].InputStream : context.Request.InputStream;
I am developing in ASP.Net 4.0 but we don't have MVC architecture. I had same issue few days back. But, I figured it out and here is my solution.
//For IE Browser
HttpPostedFile selectedfile = Request.Files[0];
System.Drawing.Bitmap obj = new System.Drawing.Bitmap(selectedfile.InputStream);
//For Non IE Browser
System.Drawing.Bitmap obj = new System.Drawing.Bitmap(Request.InputStream);
Now, you can use obj for further operation.

Resources