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.
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);
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?
Hello I have this puzzle and i would like to share and see if my solution is plausible and possibly get some help.
Basically I have a view called "Create".
Now i click "Escolher Ficheiro" (means "Choose File"), I choose the wanted file and click fill.
So far so good, my file reaches the POST method and its ok at this point.
You can see in the image below that I extract that number from the file and the button "Create" its shown.
However as you can see my "Choose File" input gets null "Nenhum fic..."(means no file selected)
This happens because I return the View Create and it refreshes my whole page.
The thing is that I need to go trhough a POST action on my Controller, so i can read that Prop1.
For obvious security reasons I cannot set a file by default, so I have tried to have some partial views and returning only that partial, but there is something im missing completly. Is there a way of refreshing the "bottom part of the view trhough ajax but going trhough the action on the controller at the same time?
Here is my controller Action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "XL2XMLProcID,SourceXLFiles,Prop1,Prop2,TargetXMLFile,State")] XL2XMLProc xL2XMLProc, HttpPostedFileBase postedFile, string submitButton)
{
if (ModelState.IsValid)
{
if (postedFile != null)
{
// fetch the date from the file
var h = new XLHelper();
var v = h.Fetch("nrContaACreditarRC", postedFile.FileName, #"C:\somefolder\somefolder\somefolder\ExcelSamples\");
if (submitButton == "Create")
{
TestZipAndUpload(v);
}
else
{
// extract postedFile data to show
xL2XMLProc.Prop1 = v;
ViewBag.comingFromFill = true;
return View(ActionName.Create.ToString(), xL2XMLProc);
}
}
else
{
// posted file is null, throw error.
return View();
}
return RedirectToAction(ActionName.Index.ToString());
}
return View(xL2XMLProc);
}
Thank you in advance.
I am trying to insert a sub class (document) of "Video" into my Organization document.
However, when I try to add a record, I get "Object reference is not set to an instance of an object."
I tried to use Add and Insert, but neither worked. I looked at the Dcoument explorer and I can see that Videos is returning "null."
I am assuming my problem is that Document DB doesn't know that Video is a list. (in my model, I have defined it as a list though)
Also, I have tried created new objects for Organization and Video. Also, I have a class called Category, it has the exact same code (except the object is Category) and it is inserting fine.
Below is the action that I am using.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "name,description,link")] Video video)
{
if (ModelState.IsValid)
{
UserSession usersession = new UserSession();
usersession = (UserSession)Session["user"];
Organization organization = (Organization)DocumentDBRepository<Organization>.GetItem(d => d.Id == usersession.organizationId);
video.DateAdded = DateTime.Now;
organization.Videos.Add(video);
await DocumentDBRepository<Organization>.UpdateItemAsync(organization.Id, organization);
return RedirectToAction("Index");
}
return View(video);
}
Set organization.Videos to a non-null value. Document db simply preserves what you stored. Apparently, you previously stored null.
I want to show the images stored in my database in my View.
I have a field "deliverable_image" from the type "longblob" in my mysql database.
In my controller:
public ActionResult Show( int id )
{
var imageData = repository.GetAllImages();
return File( imageData, "image/jpg" );
}
Repository:
public IQueryable<byte[]> GetAllImages()
{
return from deliverable in entities.deliverables
orderby deliverable.deliverable_image
select deliverable.deliverable_image;
}
View:
<img src='<%= Url.Action( "Show", "Deliverable", new { id = ViewData["DeliverableID"] } ) %>' />
But in my controller I get the error:
cannot convert from 'System.Linq.IQueryable' to 'string'
Does anybody knows how I can fix this?
In your action method, the very first line gets a collection of images (specifically, an IQueryable<byte[]>:
var imageData = repository.GetAllImages();
Then you try to send all of the images as a single file:
return File(imageData, "image/jpg");
imageData is a collection of files. You need to select one of them and return just that. To do that, you'll probably need to modify your GetAllImages function or use a different function. The action method is being passed an id, but you can't use that on the results of that function because all you have are byte arrays. You need to filter for the id specified and then use the resulting byte array.