File upload drag and drop with dropzone.js - c#

I'm using dropzone.js to implement a drag and drop feature to upload a file. And it's working. What I don't understand is why I can't make it to limit the filesize and the file extension. I have the code for it, I think, but it just won't do it.
In the cshtml page:
<script src="~/js/dropzone.js"></script>
<link href="~/css/dropzone.css" rel="stylesheet" />
<script>
Dropzone.options.dropzone = {
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 2, // MB
acceptedFiles: ".png,.jpg,.gif,.bmp,.jpeg",
accept: function (file, done) {
if (file.name == "justinbieber.jpg") {
done("Naha, you don't.");
}
else { done(); }
}
};
</script>
<div class="row">
<div class="col-md-9">
<div id="dropzone">
<form action="Upload" class="dropzone needsclick dz-clickable" id="uploader">
<div class="dz-message needsclick">
Drop files here or click to upload.<br>
</div>
</form>
</div>
</div>
</div>
And in the HomeController
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
var uploads = Path.Combine(_environment.WebRootPath, "images");
if (file.Length > 0)
using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create))
await file.CopyToAsync(fileStream);
return RedirectToAction("About");
}
I mean, the code is given by them, I have the dropzone.js and the dropzone.css like they tell us to do and still... The thing is, it's working. It uploads, goes to the code behind. Everything. I just can't apply those limitations, even though they are there...
Does anyone have an idea of what's going on here?

You've set the options on a different Dropzone than the form. From the docs:
Dropzone will find all form elements with the class dropzone, automatically attach itself to it, and upload files dropped into it to the specified action attribute.
So your <form>, with class dropzone, is automatically set up as a Dropzone.
Again from the docs, to set options on a Dropzone, you use Dropzone.options.myAwesomeDropzone = {}, where:
// "myAwesomeDropzone" is the camelized version of the HTML element's ID
In your code, you're using Dropzone.options.dropzone, so you're applying those options to a Dropzone with id dropzone. In your HTML, that's a <div>, not your existing <form> Dropzone. So when you drop a file onto your <form>, those options you've specified are not relevant - they're for a different Dropzone (which is actually never instantiated)!
Get rid of your <div id="dropzone">, and change the identifier on your options to Dropzone.options.uploader, so they apply to your form Dropzone.

Related

In ASP.net, what kind of IF statement could I use to hide a div if the image inside it matches the current page URL?

This is within Sitefinity if that matters, and I am really new at ASP.NET and C#.
I have an image-based navigation element at the bottom of a page that links to different articles using the same template. There are 5 articles, and I would like the link to the active page/article to be hidden so there is a grid of 4 image links.
Here's a screenshot:
https://i.imgur.com/PG2Sfpo.png
Here is the code behind it:
#{
string navTitle = string.Empty;
string url = string.Empty;
if (Model.CurrentSiteMapNode != null && Model.CurrentSiteMapNode.ParentNode != null)
{
if (Model.CurrentSiteMapNode.Title == "Home")
{
navTitle = Model.CurrentSiteMapNode.ParentNode.Title;
}
else
{
navTitle = Model.CurrentSiteMapNode.Title;
}
url = Model.CurrentSiteMapNode.ParentNode.Url;
}
}
<div class="foundation-stories-container">
#foreach (var node in Model.Nodes)
{
#RenderRootLevelNode(node);
}
</div>
#*Here is specified the rendering for the root level*#
#helper RenderRootLevelNode(NodeViewModel node)
{
string[] thisPage = (node.Url).Split('/');
string thisImage = thisPage[4] + ".jpg";
<a href="#node.Url" target="#node.LinkTarget">
<div class="foundation-story-block">
<div class="hovereffect">
<img src="[OUR WEBSITE URL]/stories/#thisImage" class="img-fluid">
<div class="overlay">
<h2>#node.Title</h2>
</div>
</div>
</div>
</a>
}
So we're already getting the page URL and image file name
string[] thisPage = (node.Url).Split('/');
string thisImage = thisPage[4] + ".jpg";
Is this as easy as doing the following?
if (thisImage = thisPage)
{
foundation-story-block.AddClassToHtmlControl("hide")
}
Seems easy enough, but I don't know where to start.
I'm better at Javascript, so I do have a JS solution in place for this already, but I'd really like to find a cleaner way to do it.
<script type="text/javascript">
$(document).ready(function() {
var active = window.location.pathname.split("/").pop()
var name = active;
name = name.replace(/-/g, ' ');
jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
return function( elem ) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >=
0;
};
});
$("h2:Contains('" + name + "')").closest(".foundation-story-block").addClass("hide");
});
</script>
This exists on the main template page.
Gets the last part of the URL
Sets that as a variable called "name"
Changes the dash to a space if there is one (most of the pages are associated with names so it's like /first-last)
Then it goes and looks at the which is where the title of the page lives, and if it equals the "name" variable, the ".hide" class is added to the block.
Thanks for any help anyone can provide.
You could bind a click event to your elements with the foundation-story-block class. The reason I use .on instead of .click is because when using UpdatePanels the click event won't fire after an UpdatePanel has it's update event triggered - you might encounter a similar problem with your dynamic binding so I used .on to avoid this.
$(".foundation-story-block").on("click", function() {
// Remove the "hide" class from any elements that have it applied
$.each($(".foundation-story-block.hide"), function(index, value) {
// Remove the class using the "this" context from the anonymous function
$(this).removeClass("hide");
});
// Add the "hide" class to the element that was clicked
$(this).addClass("hide");
});
I haven't run this though an IDE so it might not be 100% correct but it will put you on the correct path.
It is possible, yes. Here is how:
...
#{
var hiddenClass = thisImage == thisPage ? "hide" : string.Empty;
}
<div class="foundation-story-block #hiddenClass">
<div class="hovereffect">
<img src="[OUR WEBSITE URL]/stories/#thisImage" class="img-fluid">
<div class="overlay">
<h2>#node.Title</h2>
</div>
</div>
</div>

C# MVC HTML5 file upload control with multiple attribute duplicates images

I am having a strange issue with HTML5 file upload control with multiple attribute. I have no clue about how to fix it. I am trying to explain the problem below:
When uploading multiple images on the server, the images are uploading but some of them are getting duplicated, not by name, but by image itself.
Means if I select say 10 images, I can see 10 images are uploaded with different names (names are generated dynamically) but say 4 images are exactly the same. Means, 6 out of 10 selected images uploaded perfectly, but 4 original images got disappeared completely! But these 4 images are using contents from 6 images uploaded correctly. Moreover, this is happening in random. There is no order or sequence from what I can understand what will disappear and what will re-uploaded with a different name.
In short:
Selected 10 different images for upload
Number of images uploaded is 10
6 images (say) uploaded with correct content
4 images (say) uploaded with correct file name, but content is wrong. They are using contents of 6 images and original content of these images simply disappeared.
This is happening when I am selecting more than 5 images for upload. Anything less than 6 has no problem.
Now when I test the same code at local development environment this is not happening at all! I checked folder permission on the server and there is no restriction imposed.
I am not sure whether I could explain the issue correctly because it is really a typical one and I never encountered similar thing before !!
Markup:
<div class="row gapp-10 medium">
<div class="col-md-9">
Upload Photos <span class="small-font gray-text">(Optional)</span> <span class="small-font red-text">
(Combined upload size can't exceed 10mb.)</span>
<%:Html.TextBoxFor(model => model.VehicleImages, new {#type="file", #id="fUpload", #multiple="multiple", #class="btn btn-clc-theme-orange"}) %>
</div>
<div class="clearfix"></div>
</div>
Controller Action method:
[Authorize]
[HttpPost]
public ActionResult Upload(VehicleViewModel model)
{
if (ModelState.IsValid)
{
_service = new VehicleService();
model.SellerId = CommonMethods.LoggedInUser.SellerId;
var newVehicle = _service.Save(model);
newVehicleId = newVehicle.VehilceId;
for (var i = 0; i < Request.Files.Count; i++)
{
var fl = Request.Files[i];
if (fl == null || fl.ContentLength <= 0) continue;
var filename = System.IO.Path.GetFileName(fl.FileName.Trim());
if (string.IsNullOrEmpty(filename)) continue;
var fileExtension = System.IO.Path.GetExtension(filename);
var newFileName = String.Concat(DateTime.Now.Ticks.ToString(CultureInfo.InvariantCulture), fileExtension);
System.IO.Directory.CreateDirectory(Server.MapPath("~/VehiclePhoto/" + newVehicleId));
var photoPath = System.IO.Path.Combine(Server.MapPath("~/VehiclePhoto/" + newVehicleId + "/"), newFileName);
fl.SaveAs(photoPath);
}
}
return RedirectToAction("VehicleUploaded");
}
Edit
This is how the upload control is placed inside the form element and the method is being called. Nothing fancy, just the simple and classical way.
<% using(Html.BeginForm("Upload", "VehicleUpload", FormMethod.Post, new {enctype = "multipart/form-data"}))
{ %>
...
<div class="row gapp-10 medium">
<div class="col-md-9">
Upload Photos <span class="small-font gray-text">(Optional)</span>
<span class="small-font red-text">(Combined upload size can't exceed 10mb.)</span>
<%:Html.TextBoxFor(model => model.VehicleImages, new {#type="file", #id="fUpload", #multiple="multiple", #class="btn btn-clc-theme-orange"}) %>
</div>
<div class="clearfix"></div>
</div>
...
<input type="submit" value="Upload Vehicle" class="btn btn-primary" />
<% } %>

ASP.NET MVC rendering sections for dynamically driven content (Widgets)

I am receiving the following error in ASP.NET MVC. "the following sections have been defined but have not been rendered for the layout page" - Now I know what the error is about, but can't figure a way around it in fact it probably should work but I don't know why it won't.
I have an index.cshtml page which renders all dynamically driven DB content. Pages can contain widgets (Reusable blocks on content). The index.cshtml code is below:
#{
Layout = AppSettings.LayoutFileDirectory + "/" + PageDAL.GetLayoutFileForPage(Model.Page);
string currentSection = string.Empty;
}
#if(!string.IsNullOrEmpty(Model.Page.PageTitle))
{
<h3>#Model.Page.PageTitle</h3>
}
#Html.Raw(Model.Page.PageText)
#foreach (var item in Model.Page.WidgetsInPages)
{
//if(IsSectionDefined(item.WidgetSection))
//{
string sectionName = item.WidgetSection;
//don't want to redefine section
if (currentSection!= sectionName)
{
DefineSection(sectionName, () =>
{
//render all this sections widgets
foreach (var insider in Model.Page.WidgetsInPages.Where(w => w.WidgetSection == sectionName).OrderBy(w => w.WidgetSortOrder))
{
if (insider.Widget.WidgetFile != null)
{
Write(Html.Partial(AppSettings.WidgetTemplatesDirectory + "/" + insider.Widget.WidgetFile, item.Widget));
//Write(Html.Partial(AppSettings.WidgetTemplatesDirectory + "/" + insider.Widget.WidgetFile, new {Widget=item.Widget}));
}
}
});
currentSection = sectionName;
}
//}
}
Now I have a _DefaultTheme.cshtml with the following sections. (This is the primary layout page)
<div class="header">
#RenderSection("_HeaderSection", false)
</div>
<div class="container">
#RenderSection("_AboveBodySection", false)
#RenderBody()
#RenderSection("_BelowBodySection", false)
</div>
#RenderSection("_AfterBodySection", false)
<footer class="footer">
<div class="container">
#RenderSection("_FooterSection",false)
#Html.Raw(FrontEnd.PopulateFooter(Model.Website))
</div>
</footer>
Adding widgets to the any page which has the _DefaultTheme layout works perfectly fine, but when I start inheriting pages it becomes an issue. I now have another page _DefaultThemeArticles.cshtml the master page is _DefaultTheme.cshtml
The _DefaultThemeArticles.cshtml contains this:
#{
Layout = "~/Themes/_DefaultTheme.cshtml";
}
<div class="container">
#RenderBody()
</div>
Now the error occurs when adding Widgets to any page which has this layout. I get this error:
The following sections have been defined but have not been rendered for the layout page "~/Themes/_DefaultThemeArticles.cshtml": "_BelowBodySection".
But the section _BelowBodySection has been defined in the master page, why would it not work here?.
The problem is that in your layout page, _DefaultTheme.cshtml, by writing all of these:
#RenderSection("_HeaderSection", false)
#RenderSection("_AboveBodySection", false)
#RenderSection("_BelowBodySection", false)
#RenderSection("_AfterBodySection", false)
#RenderSection("_FooterSection",false)
You are asserting that in any child page, like _DefaultThemeArticles, there must be a section defined for each of the above. If Razor doesn't find a particular section defined (in your case _BelowBodySection) in the child page, it throws the error. You can use the following Razor to solve this:
#if (IsSectionDefined("_BelowBodySection"))
{
#RenderSection("_BelowBodySection")
}
This will only render the section if it exists.
Hope this helps.

How can I obtain selected filenames clientside using a fileupload control without submitting?

I can't find an existing answer so far, but here is my goal:
I'd like to use the asp.net fileupload control(or some other control you might suggest) to select a series of files. WITHOUT submitting a form, i'd like the selected files to be displayed in a concatenated list of some sort.
Is this possible with the Fileupload control or do I have to use something else? I've seen many answers involving submission then grabbing the filenames, but I don't want to take a trip to the server for that. Just looking to grab the filenames when they're selected, all clientside.
Thank you for your time!
Using files property. See the code below:
function onInputChange(e) {
var res = "";
for (var i = 0; i < $("#customInput").get(0).files.length; i++) {
res += $("#customInput").get(0).files[i].name + "<br />";
}
$('#result').html(res);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="customInput" type="file" accept="image/*" multiple onchange="onInputChange(event)" />
<br /><br />
<div style="color:blue" id='result'></div>
try this -
Javascript
function showFile() {
var file = document.getElementById("<%=FileUpload1.ClientID%>");
var path = file.value;
alert(path);
}
File Upload Control
<asp:FileUpload ID="FileUpload1" runat="server" onchange="showFile()" />

Convert ActionResult and PartialView IEnumerable results to return json objects

What is the best approach to take when converting a basic ActionResult to JSON objects and rendering them in a PartialView? My objective is to modify the application so that instead of the page rendering only the comments in the db at the time of the page request to a type of data service that updates thePartialView to add any incoming comments that may have been posted since the last page request. I think the solution I am looking for will use OData in json format and then bind that data using knockout.js, but not sure.
Here is the Controller ActionResult which returns an IEnumerable list of objects from the repository to a PartialView:
[ChildActionOnly]
public ActionResult GetCommentsById(int AId = 0)
{
if (AId == 0)
return HttpNotFound();
return PartialView("_CommentsPartial",
_unitOfWork.ArticleRepository.GetCommentsByArticleId(AId));
}
Here is a snippet of the PartialView to keep things short:
#model IEnumerable<BlogSite.Models.Comment>
#using BlogSite.Helpers;
<ul id="comments-list">
#{
foreach (var comment in Model)
{
<!--Grabs Parent Comment and then all replies w/ParentCommentId b4 grabs new Parent Comment -->
if (comment.isRoot && comment.ParentCommentId == null)
{
<!-- Comment -->
int counter = 0; foreach (var c in Model) { if (c.ParentCommentId == comment.CommentId) { counter += 1; } }
<li id="#comment.CommentId" itemscope itemtype="http://schema.org/UserComments" class="comment-container" tabindex="#comment.CommentId">
Then I call it from the Details view:
<div id="comments-panel" class="panel-box">
<div class="show-comments"><div id="upNdown"></div><span id="showNhide">Show Comments</span></div><br /> <br />
<div id="comments-partial" style="display:none;">
#Html.Action("AddComment", "Comment", new { AId = Model.ArticleId })
#Html.Action("GetCommentsById", "Article", new { AId = Model.ArticleId })
</div>
</div>
How can I make this conversion as painless as possible? Thanks in advance!
I think I gather from your question that the controller already did its work and that you simply want to "consume" the data output from it as if it were an AJAX request using the same js code. You can do this fairly easily by just serializing the data in the model using the Newtonsoft Json.NET api and extensions provided by Forloop.HtmlHelpers. These can be installed as nuget packages if you haven't already.
First, you would place this in your partial view
Note: If you don't want to install the Newtonsoft package you can replace JsonConvert.SerializeObject with the System.Web.Helpers method Json.Encode
#{
using (var context = Html.BeginScriptContext())
{
Html.AddScriptBlock("var jsonData=" + JsonConvert.SerializeObject(Model) + ";");
}
}
Then in your layout page, to ensure that your script block is rendered at the appropriate time, add this call to Html.RenderScripts
#Scripts.Render("~/bundles/jquery")
#*Add any other dependency scripts*#
#Html.RenderScripts()
#RenderSection("scripts", required: false)
This is why you need the Forloop.HtmlHelpers package, these extension methods help mitigate out-of-order script code getting rendered in the partial view before jQuery or anything else has started up.
Hope that helps

Categories