I have an ASP.NET MVC project and I´m using Cropbox.js : jQuery Image Crop Plugin - http://www.jqueryrain.com/demo/jquery-crop-image-plugin/ to crop an image of a user, but I cannot find how to get the cropped image to the controller.
JavaScript looks like this:
<script type="text/javascript">
window.onload = function () {
var options =
{
imageBox: '.imageBox',
thumbBox: '.thumbBox',
spinner: '.spinner',
imgSrc: 'avatar.png'
}
var cropper;
document.querySelector('#file').addEventListener('change', function () {
var reader = new FileReader();
reader.onload = function (e) {
options.imgSrc = e.target.result;
cropper = new cropbox(options);
}
reader.readAsDataURL(this.files[0]);
this.files = [];
})
document.querySelector('#btnCrop').addEventListener('click', function () {
var img = cropper.getAvatar()
document.querySelector('.cropped').innerHTML += '<img id="Portrait" src="' + img + '">';
})
document.querySelector('#btnZoomIn').addEventListener('click', function () {
cropper.zoomIn();
})
document.querySelector('#btnZoomOut').addEventListener('click', function () {
cropper.zoomOut();
})
};
</script>
I tried to use the following in the controller, but since I´m requesting the file, I´m not sure if it can even work:
HttpPostedFileBase file = Request.Files["Portrait"];
Maybe it would be possible to store the img file from javascript to the model?
My friend has solved it by adding following:
document.getElementById('avatarData').value = img;
To this part of the script:
document.querySelector('#btnCrop').addEventListener('click', function () {
var img = cropper.getAvatar()
document.querySelector('.cropped').innerHTML += '<img src="' + img + '">';
//new added
document.getElementById('avatarData').value = img;
})
Then used invisible input in View form:
<input type="hidden" id="avatarData" name="avatarData" value="">
Now I can catch it in controller:
var file = Request.Form["avatarData"];
And I´ll get:
"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQA..."
To work with this string, there is a very useful question & answer - MVC Convert Base64 String to Image, but ... System.FormatException
I don't know the jQuery Image Crop Plugin, but I think you'll need to do something like that:
Get the image's bytes, convert them to base64 and then send them to ViewController action using Ajax Post.
i thing that you can use AjaxForm or HtmlForm and push it to any action. Then use FormCollection and watch your values. For example in my Captcha generator I override some action for filter:
public class CaptchaValidator : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
RegisterViewModel model = filterContext.ActionParameters["model"] as RegisterViewModel;
if (filterContext.HttpContext.Session["Captcha"] == null || filterContext.HttpContext.Session["Captcha"].ToString() != model.Captcha)
{
filterContext.ActionParameters["captchaValid"] = false;
}
else
{
filterContext.ActionParameters["captchaValid"] = true;
}
base.OnActionExecuting(filterContext);
}
}
and use in your controller:
[CaptchaValidator]
public async Task<ActionResult> Register(RegisterViewModel model, bool captchaValid)
I think that you can here change bool captchaValid to your byte[].
I hope that it can help you :-)
Related
I am using ASP.Net Core with MVC for creating an app. I am using visual studio and IIS express currently.
Below is my current project structure:
*project directory
-wwwroot
-areas
-attachments
-controllers
-models
-views
I currently store images inside the attachments folder.
Previously I have written something like that inside my startup.cs
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "Attachments")),
RequestPath = "/Attachments"
});
I have also done something like this below:
appendImage(#Url.Content("~/Attachments/")+result.fileName);
I did this to display an image on my view. The image is displayed successfully.
What I am trying to achieve now is the on the UI allow the user to make a choice to delete the files inside that attachments folder
I tried the following code:
string contentRootPath = _hostingEnvironment.ContentRootPath;
string fullImagePath = Path.Combine(contentRootPath + "\\Attachments", currentItemToDelete.FileName);
if (System.IO.File.Exists(fullImagePath))
{
try{
System.IO.File.Delete(fullImagePath);
}catch(Exception e){
operationResult = "Attachment Path. Internal Server Error";
}
}
The execution does enter the if (System.IO.File.Exists(fullImagePath))
but it raises an exception when it reaches System.IO.File.Delete. The exception states that the file which resides in that path is being used by another process. And thus I cannot delete the file. The only process that is accessing the file is the web app I am creating/debugging at the same time. How do I prevent this exception from happening? Do I have to use other kind of code to delete the file ?
EDIT to include more details:
Inside my view(index.cshtml):
appendImage is a javascript function:
function appendImage(imgSrc) {
var imgElement = document.createElement("img");
imgElement.setAttribute('src', imgSrc);
if (imgSrc.includes(null)) {
imgElement.setAttribute('alt', '');
}
imgElement.setAttribute('id', "img-id");
var imgdiv = document.getElementById("div-for-image");
imgdiv.appendChild(imgElement);
}
That function is called below:
$.ajax({
url:'#Url.Action("GetDataForOneItem", "Item")',
type: "GET",
data: { id: rowData.id },
success: function (result) {
removeImage();
appendImage(#Url.Content("~/Attachments/")+result.fileName);
$("#edit-btn").attr("href", '/Item/EditItem?id=' + result.id);
},
error: function (xhr, status, error) {
}
});
After calling appendImage(); I change the href of a <a> tag. When the user clicks on the link, the user is directed to another page(edit.cshtml). In the page, the image which resides in that path is also being displayed with code like this:
<img src="#Url.Content("~/Attachments/"+Model.FileName)" alt="item image" />
In this new page(edit.cshtml), there is a delete button. Upon clicking the delete button, the execution of the program goes to the controller which is this controller function:
[HttpPost]
public string DeleteOneItem(int id)
{
//query the database to check if there is image for this item.
var currentItemToDelete = GetItemFromDBDateFormatted(id);
if (!string.IsNullOrEmpty(currentItemToDelete.FileName))
{
//delete the image from disk.
string contentRootPath = _hostingEnvironment.ContentRootPath;
string fullImagePath = Path.Combine(contentRootPath + "\\Attachments", currentItemToDelete.FileName);
if (System.IO.File.Exists(fullImagePath))
{
try
{
System.IO.File.Delete(fullImagePath);
}catch(Exception e)
{
}
}
}
return "";
}
EDIT to answer question:
Add in
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
before system.io.file.delete
you can replace your C# method DeleteOneItem with this given code. may be it might work.
[HttpPost]
public string DeleteOneItem(int id)
{
//query the database to check if there is image for this item.
var currentItemToDelete = GetItemFromDBDateFormatted(id);
if (!string.IsNullOrEmpty(currentItemToDelete.FileName))
{
//delete the image from disk.
string contentRootPath = _hostingEnvironment.ContentRootPath;
string fullImagePath = Path.Combine(contentRootPath + "\\Attachments", currentItemToDelete.FileName);
if (System.IO.File.Exists(fullImagePath))
{
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.IO.File.Delete(fullImagePath);
}
catch (Exception e) { }
}
}
return "";
}
try
{
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.IO.File.Delete(fullImagePath);
}
catch(Exception e){
}
I'm try to upload image in database, i'm using drobzone.js
that's my controller code
[HttpGet]
public ActionResult Show(int? id)
{
string mime;
byte[] bytes = LoadImage(id.Value, out mime);
return File(bytes, mime);
}
[HttpPost]
public ActionResult Upload()
{
SuccessModel viewModel = new SuccessModel();
if (Request.Files.Count == 1)
{
var name = Request.Files[0].FileName;
var size = Request.Files[0].ContentLength;
var type = Request.Files[0].ContentType;
viewModel.Success = HandleUpload(Request.Files[0].InputStream, name, size, type);
}
return Json(viewModel);
}
private bool HandleUpload(Stream fileStream, string name, int size, string type)
{
bool handled = false;
try
{
byte[] documentBytes = new byte[fileStream.Length];
fileStream.Read(documentBytes, 0, documentBytes.Length);
Pictures databaseDocument = new Pictures
{
ProfilePicture=documentBytes,
FName=name,
Size=size,
Type=type
};
using(var contxt=new EnglisCenterEntities())
{
contxt.Pictures.Add(databaseDocument);
handled = (contxt.SaveChanges() > 0);
}
}
catch (Exception )
{
// Oops, something went wrong, handle the exception
}
return handled;
}
private byte[] LoadImage(int id, out string type)
{
byte[] fileBytes = null;
string fileType = null;
using(var contxt=new EnglisCenterEntities())
{
var databaseDocument = contxt.Pictures.FirstOrDefault(doc => doc.IdPicture == id);
if (databaseDocument != null)
{
fileBytes = databaseDocument.ProfilePicture;
fileType = databaseDocument.Type;
}
}
type = fileType;
return fileBytes;
}
and this is my script
<script type="text/javascript">
$(document).ready(function () {
$("#preview").fadeOut(15);
$("#refreshButton").click(function () {
var imageToLoad = $("#imageId").val();
if (imageToLoad.length > 0) {
$("#preview").attr("src", "/Document/Show/" + imageToLoad);
$("#preview").fadeIn();
}
});
});
and this is my view
<form action="/Document/Upload" class="dropzone" id="my-awesome-dropzone"></form>
<input type="text" name="imageId" id="imageId" />
<button type="button" id="refreshButton">Update Image</button>
<img src="/" style="display: none" id="preview" />
and it's working with multi images but i want to save single image and prevent the user put more than one image. Is there a way to save a single image and to prevent user put more than an image using dropzone.js?
Javascript is needed to limit maxFiles, see http://www.dropzonejs.com/#configuration-options and http://jsfiddle.net/P2dTF/2/ for example:
Dropzone.autoDiscover = true;
Dropzone.options.my-awesome-dropzone = {
maxFiles: 1
};
[HttpPost]
public ActionResult Upload(HttpPostedFileBase file)
{
SuccessModel viewModel = new SuccessModel();
if (file != null)
{
viewModel.Success = HandleUpload(file);
}
return Json(viewModel);
}
Param name of file is important, dropzone binds single upload to param file (and multiple to a param array of files). Don't see why you need a fileStream though, fileStream is needed when you want to return a range of bytes for example with a Request Header (audio) for partial download, HttpPostedFileBase does the job in your case.
private bool HandleUpload(HttpPostedFileBase file)
{
bool handled = false;
try
{
byte[] documentBytes = new byte[file.ContentLength];
Pictures databaseDocument = new Pictures
{
ProfilePicture=documentBytes,
FName=file.FileName,
Size=file.ContentLength,
Type=file.ContentType
};
using(var contxt=new EnglisCenterEntities())
{
contxt.Pictures.Add(databaseDocument);
handled = (contxt.SaveChanges() > 0);
}
}
catch (Exception )
{
// Oops, something went wrong, handle the exception
}
return handled;
}
I have an ActionMethod which can return a Memorystream or byte[] of an image. This action method is called on ajax. Now I want to bind this image to an image tag src attribute.
public ActionResult GetBpChart(string Timespan)
{
var ObjMemoryStream = new MemoryStream();
try
{
ObjMemoryStream = MyModel.GetImage(Timespan);
}
catch (Exception Ex)
{
}
//return Content(Convert.ToBase64String(ObjMemoryStream.GetBuffer()));
//return Json(ObjMemoryStream.GetBuffer());
return File(ObjMemoryStream.GetBuffer(), "image/jpeg");
}
function GetChart() {
try {
$.ajax({
type: 'POST',
url: "myActionMethodurl",
data: {
Timespan: $('#ddlBpTimespan').val()
},
success: function (ImgSrc) {
// For Base64String
//$('#divBpChart').innerHTML = '<img src= "data:image/jpeg;base64," ' + ImgSrc + '"/>';
// For Json of byte[]
//$('#divBpChart').innerHTML = '<img src= "data:image/gif;base64," ' + ImgSrc + '"/>';
// For FileContentResult
$('#imgBpChart').attr('src', ImgSrc);
HideAjaxProgress();
}
});
} catch (E) {
}
}
I tried the above 3 combinations. But no luck. Can someone say what I am missing here or what's the correct way to achieve this
From reading the .ajax() docs, I would say the simple answer is "no, you can't do that."
Luckily, you don't need to. Just create the relevant URL and update the target image's src attribute:
function GetChart() {
try {
var ImgSrc = '/mycontroller/myActionMethodurl?timespan=' + $('#ddlBpTimespan').val();
$('#imgBpChart').attr('src', ImgSrc);
} catch (error) {
}
}
If you're coding this within a View, you can leverage the UrlHelper to help build the correct "base" URL:
function GetChart() {
try {
var ImgSrc = '#Url.Action("myActionMethodurl", "mycontroller")?timespan=' + $('#ddlBpTimespan').val();
$('#imgBpChart').attr('src', ImgSrc);
} catch (error) {
}
}
Perhaps a better alternative?
For a plain JavaScript method that seems to work, refer to Using jQuery's ajax method to retrieve images as a blob.
I am working a jQuery file upload helper and I need to understand how can I append the File from the form or the Form data as a whole to the request.
I have worked with the ASP.NET code to accept image from the Request and handle the further code, but when I try to use it using jQuery $.ajax() I can't get it to work.
I have been though Stack Overflow questions, and I have tried using FormData appending the data from the input[type="file"] (input for the file element). But each time (on the server) the block that is executed that tells me there is no file with the request.
Here is the ASP.NET code (UploadFile page)
#{
var fileName = "Not running!";
if(IsPost) {
if(Request.Files.Count > 0) {
var image = WebImage.GetImageFromRequest();
fileName = Path.GetFileName(image.FileName) + " From server";
} else {
fileName = "No file attached! From Server";
}
}
Response.Write(fileName);
}
The jQuery code is as
$(document).ready(function () {
$('form input[type=submit]').click(function () {
event.preventDefault();
$.ajax({
url: '/UploadFile',
data: new FormData().append('file',
document.getElementById("image").files[0]),
type: 'POST',
success: function (data) {
$('#result').html('Image uploaded was: ' + data);
}
});
});
});
I am already scratching my head since I can't get the file content on the serverside.
How can I send the file to the server, or the entire form data to the server, anything would be welcome!
Try using handler for this and Newtonsoft.json.dll for these purpose.
For jQuery
(document).ready(function () {
$('form input[type=submit]').click(function () {
event.preventDefault();
$.ajax({
url: 'handler.ashx', // put a handler instead of direct path
data: new FormData().append('file',
document.getElementById("image").files[0]),
type: 'POST',
success: function (data) {
$('#result').html('Image uploaded was: ' + data);
}
});
});
});
In asp.net
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.Web;
using Newtonsoft.Json;
public class Handler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
HttpPostedFile up = context.Request.Files[0];
System.IO.FileInfo upinfo = new System.IO.FileInfo(up.FileName);
System.Drawing.Image upimg = System.Drawing.Image.FromStream(up.InputStream);
string path = context.Server.MapPath("~/temp"); // this is the server path where you'll be saving your image
if (!System.IO.Directory.Exists(path))
System.IO.Directory.CreateDirectory(path);
string fileName;
fileName = up.FileName;
string newFilename = Guid.NewGuid().ToString();
System.IO.FileInfo fInfo = new System.IO.FileInfo(fileName);
newFilename = string.Format("{0}{1}", newFilename, fInfo.Extension);
string strFileName = newFilename;
fileName = System.IO.Path.Combine(path, newFilename);
up.SaveAs(fileName);
successmsg1 s = new successmsg1
{
status = "success",
url = "temp/" + newFilename,
width = upimg.Width,
height = upimg.Height
};
context.Response.Write(JsonConvert.SerializeObject(s));
}
How can I upload text written by the user to an asp.net generic handler? the text is pretty lengthy.
Try some jQuery code like this:
jQuery(document).ready(function($){
$('#button-id-to-submit-info-to-the-handler').on('click', function(ev) {
ev.preventDefault();
//Wrap your msg in some fashion
//in case you want to end other things
//to your handler in the future
var $xml = $('<root />')
.append($('<msg />', {
text: escape($('#id-of-your-textarea-that-has-the-text').val())
}
));
$.ajax({
type:'POST',
url:'/path/to-your/handler.ashx',
data: $('<nothing />').append($xml).html(),
success: function(data) {
$('body').prepend($('<div />', { text: $(data).find('responsetext').text() }));
}
});
});
});
And in your handler:
public class YourHandler : IHttpHandler
{
public void ProcessRequest(HttpContext ctx)
{
//Response with XML
//Build a response template
ctx.Response.ContentType = "text/xml";
String rspBody = #"<?xml version=\""1.0\"" encoding=\""utf-8\"" standalone=\""yes\""?>
<root>
<responsetext>{0}</responsetext>
</root>";
//Get the xml document created via jquery
//and load it into an XmlDocument
XmlDocument xDoc = new XmlDocument();
using (System.IO.StreamReader sr = new StreamReader(ctx.Request.InputStream))
{
String xml = sr.ReadToEnd();
xDoc.LoadXml(xml);
}
//Find your <msg> node and decode the text
XmlNode msg = xDoc.DocumentElement.SelectSingleNode("/msg");
String msgText = HttpUtility.UrlDecode(msg.InnerXml);
if (!String.IsNullOrEmpty(msgText))
{
//Success!!
//Do whatever you plan on doing with this
//and send a success response back
ctx.Response.Write(String.Format(rspBody, "SUCCESS"));
}
else
{
ctx.Response.Write(String.Format(rspBody, "FAIL: msgText was Empty!"));
}
}
}
Looking at the code from your comment (thanks for sharing), it seems like the parameter containing your text is called data in your JavaScript and you are looking for file in your handler.
Try: context.Request.Form["data"] in your handler.