I tried to add a custom column to Telerik Grid for uploading documents, but I am receiving 0 files in controller.
I think it's because name of form input field is different than parameter I receive in controller, Telerik adds column name (Document1) to left of upload filed name, see screenshot.
And this is my controller, see the parameter is file but in source of HTML the input field name is Document1.file
[HttpPost]
public async Task<ActionResult> SaveAsync(IEnumerable<IFormFile> file)
{
// The Name of the Upload component is "files"
if (file != null)
{
foreach (var f in file)
{
var fileContent = ContentDispositionHeaderValue.Parse(f.ContentDisposition);
// Some browsers send file names with full path.
// We are only interested in the file name.
var fileName = Path.GetFileName(fileContent.FileName.ToString().Trim('"'));
var physicalPath = Path.Combine(new HostingEnvironment().WebRootPath, "App_Data", fileName);
// The files are not actually saved in this demo
using (var fileStream = new FileStream(physicalPath, FileMode.Create))
{
await f.CopyToAsync(fileStream);
}
}
}
// Return an empty string to signify success
return Content("");
}
But I can't add a dot to parameter in controller!
How can I force it to for example name parameter as Document1_file?
Related
I am creating an app where user can upload their text file and find out about its most used word.
I have tried to follow this doc to get used to the idea of using AZURE STORAGE BLOBS - https://learn.microsoft.com/en-us/azure/storage/blobs/storage-quickstart-blobs-dotnet
But I am super newbie and having a hard time figuring it out how to adapt those blobs methods for my POST method.
This my sudo - what I think I need in my controller and what needs to happen when POST method is triggered.
a.No need for DELETE or PUT, not replacing the data nor deleting in this app
b.Maybe need a GET method, but as soon as POST method is triggered, it should pass the text context to the FE component
POST method
connect with azure storage account
if it is a first time of POST, create a container to store the text file
a. how can I connect with the existing container if the new container has already been made? I found this, but this is for the old CloudBlobContainer. Not the new SDK 12 version.
.GetContainerReference($"{containerName}");
upload the text file to the container
get the chosen file's text content and return
And here is my controller.
public class HomeController : Controller
{
private IConfiguration _configuration;
public HomeController(IConfiguration Configuration)
{
_configuration = Configuration;
}
public IActionResult Index()
{
return View();
}
[HttpPost("UploadText")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
if (files != null)
{
try
{
string connectionString = Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING");
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
string containerName = "textdata" + Guid.NewGuid().ToString();
BlobContainerClient containerClient = await blobServiceClient.CreateBlobContainerAsync(containerName);
//Q. How to write a if condition here so if the POST method has already triggered and container already created, just upload the data. Do not create a new container?
string fileName = //Q. how to get the chosen file name and replace with newly assignmed name?
string localFilePath = //Q. how to get the local file path so I can pass on to the FileStream?
BlobClient blobClient = containerClient.GetBlobClient(fileName);
using FileStream uploadFileStream = System.IO.File.OpenRead(localFilePath);
await blobClient.UploadAsync(uploadFileStream, true);
uploadFileStream.Close();
string data = System.IO.File.ReadAllText(localFilePath, Encoding.UTF8);
//Q. If I use fetch('Home').then... from FE component, will it receive this data? in which form will it receive? JSON?
return Content(data);
}
catch
{
//Q. how to use storageExeption for the error messages
}
finally
{
//Q. what is suitable to execute in finally? return the Content(data) here?
if (files != null)
{
//files.Close();
}
}
}
//Q. what to pass on inside of the Ok() in this scenario?
return Ok();
}
}
Q1. How can I check if the POST method has been already triggered, and created the Container? If so how can I get the container name and connect to it?
Q2. Should I give a new assigned name to the chosen file? How can I do so?
Q3. How can I get the chosen file's name so I can pass in order to process Q2?
Q4. How to get the local file path so I can pass on to the FileStream?
Q5. How to return the Content data and pass to the FE? by using fetch('Home').then... like this?
Q6. How can I use storageExeption for the error messages
Q7. What is suitable to execute in finally? return the Content(data) here?
Q8. What to pass on inside of the Ok() in this scenario?
Any help is welcomed! I know I asked a lot of Qs here. Thanks a lot!
Update: add a sample code, you can modify it as per your need.
[HttpPost]
public async Task<IActionResult> SaveFile(List<IFormFile> files)
{
if (files == null || files.Count == 0) return Content("file not selected");
string connectionString = "xxxxxxxx";
BlobServiceClient blobServiceClient = new BlobServiceClient(connectionString);
string containerName = "textdata" + Guid.NewGuid().ToString();;
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
containerClient.CreateIfNotExists();
foreach (var file in files)
{
//use this line of code to get file name
string fileName = Path.GetFileName(file.FileName);
BlobClient blobClient = containerClient.GetBlobClient(fileName);
//directly read file content
using (var stream = file.OpenReadStream())
{
await blobClient.UploadAsync(stream);
}
}
//other code
return View();
}
Original answer:
When using List<IFormFile>, you should use foreach code block to iterate each file in the list.
Q2. Should I give a new assigned name to the chosen file? How can I do
so?
If you want to keep the file original name, in the foreach statement like below:
foreach (var file in myfiles)
{
Path.GetFileName(file.FileName)
//other code
}
And if you want to assign a new file name when uploaded to blob storage, you should define the new name in this line of code: BlobClient blobClient = containerClient.GetBlobClient("the new file name").
Q3. How can I get the chosen file's name so I can pass in order to
process Q2?
refer to Q2.
Q4. How to get the local file path so I can pass on to the FileStream?
You can use code like this: string localFilePath = file.FileName; to get the path, and then combine with the file name. But there is a better way, you can directly use this line of code Stream uploadFileStream = file.OpenReadStream().
Q5. How to return the Content data and pass to the FE? by using
fetch('Home').then... like this?
Not clear what's it meaning. Can you provide more details?
Q6. How can I use storageExeption for the error messages
The storageExeption does not exist in the latest version, you should install the older one.
You can refer to this link for more details.
#Ivan's answer is what the documentation seems the recommend; however, I was having a strange issue where my stream was always prematurely closed before the upload had time to complete. To anyone else who might run into this problem, going the BinaryData route helped me. Here's what that looks like:
await using var ms = new MemoryStream();
await file.CopyToAsync(ms);
var data = new BinaryData(ms.ToArray());
await blobClient.UploadAsync(data);
So I am trying to make a post (for example like those on Facebook) which contain a title, description and an image. I do this using Angular and ASP.NET CORE. My DB structure is that I save my image on the wwwroot server folder and want to create a Table 'posts' that contains a link to this folder for getting the image.
Creating a post and sending an image both work, but now I want to send my Object 'Post' and my image (FormData) in one HttpPost. However it doesn't seem to work.
Frontend Angular
onSubmit(Image) {
let post = new Post(this.post.value.titel, this.post.value.omschrijving, new Date(), this._userService.user$.getValue())
this._postService.voegPostToe(post, this.selectedFile).subscribe();
this.post.reset();
}
voegPostToe(post: Post, fileToUpload: File) {
const formData: FormData = new FormData();
formData.append('Image', fileToUpload, fileToUpload.name);
formData.append('ObjectData', JSON.stringify(post));
return this.http.post(`${environment.apiUrl}/posts`, formData);
}
Backend ASP.NET
[HttpPost]
[Authorize(Roles = "Admin")]
public async Task<ActionResult<Post>> PostPost()
{
Post resource = JsonConvert.DeserializeObject<Post>(HttpContext.Request.Form["ObjectData"]);
var file = HttpContext.Request.Form.Files["Image"];
string fileName = new String(Path.GetFileNameWithoutExtension(file.FileName).Take(10).ToArray()).Replace(" ", "-");
fileName = fileName + DateTime.Now.ToString("yymmssfff") + Path.GetExtension(file.FileName);
string filePath = _env.WebRootPath + "\\Images\\" + fileName;
using (var fileSrteam = new FileStream(filePath, FileMode.Create))
{
await file.CopyToAsync(fileSrteam);
}
resource.Fotolink = filePath;
_postRepository.Add(resource);
_postRepository.SaveChanges();
return NoContent();
}
My problem is that I don't know how to convert the stringified Object to the Object's class in ASP.NET, I always seem to get a BadRequest error.
public string GetRazorViewAsString(string filePath, object model = null)
{
var resultString = new StringWriter();
var context = new HttpContextWrapper(HttpContext.Current);
var routeData = new RouteData();
// Creatign the controller context
var controllerContext = new ControllerContext(new RequestContext(context, routeData), new DummyController());
// Rebdering the view and getting the html to resultString
var razor = new RazorView(controllerContext, filePath, null, false, null);
razor.Render(new ViewContext(controllerContext, razor, new ViewDataDictionary(model), new TempDataDictionary(), resultString), resultString);
// Returning the generated html
return resultString.ToString();
}
public class DummyController : Controller { }
Currently, we are using above code for generating HTML for a view.
In that, view path is a virtual path.
Now, we are planning to move the views outside of the project. So keeping virtual path is not possible now.
Is there any way of creating HTML by taking full path of the view
You can implement a VirtualPathProvider. Create a class that inherits from VirtualPathProvider, then override two methods:
FileExists
GetFile
These methods accept a virtual path argument, which you can then map to some location on disk. Your controllers will be unaware that this provider exists (they continue to use virtual paths for views). You may need to also implement a VirtualFile.
For more information, see http://www.umbraworks.net/bl0g/rebuildall/2009/11/17/ASP_NET_MVC_and_virtual_views. This blog post is sourcing views from a database, but you can adapt it to source views from anywhere.
I have a page with a picture and few other fields . Uploading picture works fine.If user wants to edit and put a different picture, that works fine too. The problem occurs when user edit a different field (other than the image field) in the record . After saving , the image disappears.
Here is my controller
public ActionResult Edit([Bind(Include = "GlobalMaterialId,Length,Picture")] MetalStock metalStock, HttpPostedFileBase ImageFile)
{
if (ModelState.IsValid)
{
if (ImageFile != null)
{
string pic = System.IO.Path.GetFileName(ImageFile.FileName);
metalStock.ImagePath = pic;
using (MemoryStream ms = new MemoryStream())
{
ImageFile.InputStream.CopyTo(ms);
metalStock.Picture = ms.GetBuffer();
}
}
m_db.Entry(metalStock).State = EntityState.Modified;
m_db.SaveChanges();
return RedirectToAction("Index");
}
return View(metalStock);
}
here is the image uploading bit of the view
<input name="ImageFile" type='file' / >
I understand that when I edit a field and save, ImageFile that is passed to the controller is empty and that creates the problem. I tried few other things such as trying to get the picture from the record and reassigning it to the object etc. Non of them worked. Please help.
I would guess your controller dies because it looks for a HttpPostedFile and does not get one.
You can either make it nullable in the declaration of your action
public ActionResult Edit([Bind(Include = "GlobalMaterialId,Length,Picture")] MetalStock metalStock, HttpPostedFileBase ImageFile = null)
or don't declare it in the controller at all and pick it up from the Request:
var Image = Request.Files["ImageFile"]
You could store your existing image file in a hidden field as a base64 string and in your post action check to see if the HttpPostedFileBase is null (they haven't selected a different image). If it is, convert your base64 string back to a byte array and assign it to your metalStock.Picture property.
This is how I've handled this scenario in the past. You will need to create another property on your viewmodel to hold this base64 string. This is beneficial assuming your view is not bound directly to your entity, but a viewmodel instead.
After I uploaded file using file uplaod control, and assigning it's name in controller, i need to get that file name in view. Also with that file name how to delete that file in my local drive(which is previously uploaded).
thanks.
check it please
public ActionResult SaveFile(HttpPostedFileBase FileUpload)
{
string path = string.Empty;
if (FileUpload != null)
if (FileUpload.ContentLength > 0)
{
fileName = Path.GetFileName(FileUpload.FileName);
path = Path.Combine(Server.MapPath("~/App_Data/Documents"), fileName);
FileUpload.SaveAs(path);
}
return View();
}
and don't forget to add enctype = "multipart/form-data" attribute to form
to can use this jQuery plugin to upload file via ajax jQuery File Upload in ASP.NET MVC