Using Slim Image Upload with ASP.NET Core Form Post
https://pqina.nl/slim/
View Model
Public Boolean HasImage {get;set;} // Used to preload existing image and to know if image has been removed on post back.
Public string Image {get;set;} // Holder for the Image Object to come back through the form post
Public Guid Guid {get;set;} // Guid (generated on first postback but supplied back to display existing image on reloading form. Can use same Guid to save in azure blob storage etc.
Pubic string Description {get;set;} // other field example
Public string Notes {get;set;} // other field example
View
Add hidden fields to form postback
<input id="HasImage" asp-for="HasImage" hidden />
<input asp-for="Guid" id="Guid" hidden />
Add div class for slim. Style as necessary for width / height or do that dynamically. Can by dynamically scaled, and positioned.
<div class="slim"></slim>
Javascript for dynamically resizing inside a bootstrap panel. SlimHolder is the panel pane. The javascript runs on the window.resize.
var myWidth = $('#slimHolder').width();
var myHeight = $('#slimHolder').height();
var mySize = 0;
if (myWidth < myHeight) {
mySize = myWidth;
}
else {
mySize = myHeight;
}
$('.slim').height(mySize);
$('.slim').width(mySize);
var myLeftOffset = (myWidth - mySize) / 2;
if (myLeftOffset > 0) {
$('.slim').css({ 'marginLeft': myLeftOffset + "px" });
}
else {
$('.slim').css({'marginLeft': 0 + "px"});
}
Slim Initialisation
This can be done on page load or when the slim is bought into view. For example if the slim div is on a pane of a tab panel the initialisation can be executed when the tab pane is brought into view and a flag checked to see if the slim div has already been initialised. The Download Image is an action of the Controller that returns the image. If an existing image exists, the image is loaded by slim. After load the “Image” holder is cleared to prevent the existing image loaded being posted back with the form data.
$('.slim').slim(
{
didRemove: ImageRemoved,
defaultInputName: "Image",
label: "Drop image here or click to select file",
ratio: "1:1",
minSize: "100,100",
size: "600,600",
forceType:"jpg"
});
ImageInitialised = true;
if (document.getElementById("HasImage")).checked) {
let myImageName: string = (document.getElementById("Guid")).val();
if (myImageName != null && myImageName.length > 0) {
$('.slim').slim('load', `/Image/DownloadImage/${myImageName}`,
function (error,data) {
$('[name="Image"]').val(null);
});
}
}
}
function ImageRemoved() {
$('#HasImage').prop('checked', false);
}
Called after save successful to prevent reposting the image on subsequent saves
function ClearImageUpload() {
if (ImageInitialised) {
$('[name="Image"]').val(null);
}
}
Download Image Controller
Gets the image from a memory stream and returns the FileStreamResult which slim reads and loads into the image. Note it’s also possible to provide slim with a direct URL to where the image is stored rather than going via a controller if the image is physically available by a direct URL.
[Route("/Image/DownloadImage/{Guid:Guid}")]
public FileStreamResult DownloadImage(Guid guid)
{
var myImageFile = File(ImageStorage.GetImageStream(guid), "image/jpeg", $"{guid}.jpg");
return myImageFile;
}
Async Form Postback Controller
In the sample given the image is stored in a memory stream but can be streamed directly to Azure Blob Storage or other stream provider to save the image.
[HttpPost]
[ValidateAntiForgeryToken]
public JsonResult UpdateImage(ImageViewModel image)
{
if (ModelState.IsValid)
{
if(ImageViewModel.Image!=null)
{
var myImageResult = JsonConvert.DeserializeObject<ImageResult>(InventoryItem.Image);
if (myImageResult.output != null)
{
using (var myStream = new MemoryStream())
{
myImageResult.GetImageStream().CopyTo(myStream);
}
image.HasImage = true;
Image.Guid = Guid.NewGuid();
}
}
// need to save the image view model here to keep the other associated data.
// Save view model back to a data model or other (not provided in example)
// Next return back to the form ajax data that can be processed back on client side like the new Guid, or a more advanced object indicating save state etc.
return Json(Image.Guid);
}
}
}
return Json(false); // save failed return back an object, or some other status that can be read in the ajax success.
}
Image Result Class
The class that the JSON Object created by slim is deserialized into.
public class ImageResult
{
public MemoryStream GetImageStream()
{
byte[] bytes = Convert.FromBase64String(output.image.Substring(output.image.IndexOf(",")+1));
return new MemoryStream(bytes);
}
public Bitmap GetBitmap()
{
return new Bitmap(GetImageStream());
}
public ImageResultInput input { get; set; }
public ImageResultOutput output { get; set; }
public class ImageResultInput
{
public string name { get; set; }
public string type { get; set; }
public int? width { get; set; }
public int? height { get; set; }
public int? size { get; set; }
}
public class ImageResultOutput
{
public string name { get; set; }
public string type { get; set; }
public int? width { get; set; }
public int? height { get; set; }
public string image { get; set; }
}
}
}
The javascript AJAX Form Post Back method
function Submit() {
var myData = $(‘#formName}’).serializeArray();
if (this.Validate()) {
$.ajax({
type: 'POST',
url: ‘/Image/UpdateImage/’,
data: myData,
timeout:600000,
dataType: 'json',
success: (data) => { function() { ClearImageUpload(); // do whatever },
error: (data) => { // do whatever},
complete: () => {
// do whatever
}
}
);
}
}