Reduce Image Size Submitted Via Webform - javascript

My company uses web forms on a company website to submit tickets on issues. This is run through a C# ASP.NET application that uses a MVC structure.
These forms have an option to upload photos with them. The issue here is many of these photos are quite large, usually 5-15 MB.
Is there a way to reduce the size of these files before they get stored into sql server?
Here are some code snippets of how we are currently storing the photos.
I changed some of the naming for the purpose of posting.
It all starts at the form where there is the submit area
<legend><b>Upload Photos</b> </legend>
<div class="buttons">
#ViewBag.Result
<table>
<tr>
<th>
<label asp-for="#Model.Photos.NewPhotoFiles">New Photo Files</label>
</th>
<td>
<input asp-for="#Model.Photos.NewPhotoFiles" type="file" multiple="" />
<span asp-validation-for="#Model.Photos.NewPhotoFiles"></span>
</td>
</tr>
</table>
<br />
</div>
When the form gets submitted the new photo hits a method within the controller that redirects the request to a service.
public void UpdatePhotos(PhotoViewModel viewmodel)
{
if (viewmodel == null)
{
return;
}
// Remove Photos
using var context = new Context();
var TicketId = viewmodel.TicketId;
if (TicektId != 0)
{
foreach (var pic in context.Photo.Where(x => x.TicketId == TicketId))
{
if (!viewmodel.PhotosBase64.Contains(Convert.ToBase64String(pic.Photo1)))
{
context.Photo.Remove(pic);
}
else if (viewmodel.PhotosBase64 == null)
{
context.Photo.Remove(pic);
}
}
context.SaveChanges();
}
// Add New Photos
if (viewmodel.NewPhotoFiles != null)
{
foreach (var pic in viewmodel.NewPhotoFiles)
{
using var memoryStream = new MemoryStream();
pic.CopyTo(memoryStream);
if (!context.Photo.AsNoTracking().Where(x => x.TicketId == TicketId && x.Photo1 == memoryStream.ToArray()).Any())
{
var picture = new Photo
{
TicketId = TicketId,
Photo1 = memoryStream.ToArray()
};
context.Photo.Add(picture);
context.SaveChanges();
}
}
}
}
Here are some other questions people have asked that are similar but either the question was not figured out or I had trouble translating their solution to my logic/language
Reduce Image Size Before Uploading
Reduce image size before uploading to firebase storage

Related

dynamically convert table values on loading

I have a html table that lists data from data from a database. One of the entries is the file size in bytes. I want to display the value in the appropriate kb/mb/gb values.
I found a script here to make the conversion.
Below is my attempt at implementing the script, however it is not working.
The desired end state is to have a value like 3597671 Bytes display as 3.43 MB.
<script>
function SizeSuffix(a, b = 2, k = 1024) { with (Math) { let d = floor(log(a) / log(k)); return 0 == a ? "0 Bytes" : parseFloat((a / pow(k, d)).toFixed(max(0, b))) + " " + ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"][d] } }
function setSize(size, id) {
document.getElementById(id).innerText = SizeSuffix(size);
}
window.onload = function () {
var sizedata = document.getElementsByName("Size");
for (size in sizedata) {
bytes = size.innerText;
setSize(bytes, size.id);
}
}
</script>
#foreach (var History in Model)
{
<tr>
<td>#History.Date</td>
<td>#History.Path</td>
<td>#History.File</td>
<td name="Size" id="#History.Id">#History.FileSize</td>
</tr>
}
EDIT: I came up with a different solution. I am working with a partial view in razor pages. What I've done is pass the page model of the page that calls the partial view. I included a function in that page model that performs the transformation.
I replaced the line
<td name="Size" id="#History.Id">#History.FileSize</td>
with
<td>#Model.SizeSuffix((ulong)History.FileSize)</td>
and removed the script block. This performs the calculation when ever the partial view is loaded and supports the infinite scrolling code.

How to Check/Unchecked the check box list by retrieving the data from database in ASP.NET MVC

i am very new to ASP.NET MVC and trying to make a project tracking tool.
i want to show a list of Questions from a table when page loads and it works.
but when a user click on the "Select Category Name" he should get all Questions as above and a check mark on the Questions which are related to specific category.as of now i am filtering and getting only the filtered Questions which is correct but the list should show all other categories questions as unchecked.
i have a table for Questions, Category and a one to many relationship between both of them hence i made a mapping table named QuestionCategoryMapping Table.
the problem i am facing that i don't know how to filter first all the questions and then later put a checkbox when a user select from list.
i have made two separate functions both are working but i need the separated functionality together.
here are my code spinets.
// test code for partial view in project
QuestionDataAccessLayer questionDataAccessLayer = new QuestionDataAccessLayer();
//controller to add a partial view in create and edit question page
public new PartialViewResult PartialView()
{
IEnumerable<Questions> questions = questionDataAccessLayer.GetAllQuestion();
return PartialView("Partial_View", questions);
}
QuestionCategoryDataAccessLayer questionCategoryDataAccess = new QuestionCategoryDataAccessLayer();
public new PartialViewResult PartialView1(int id)
{
IEnumerable<QuestionCategory> questionCategory = questionCategoryDataAccess.GetQuestionsPerCategory(id);
return PartialView("Partial_View1", questionCategory);
}
the JavaScript,HTML files are as below:
<script>
// code to update the partial view
window.onload = function () {
$.ajax({
url: 'PartialView',
data: {},
type: 'Get',
success: function (response) {
$("#Questionpartial").html(response)
}
});
}
//code to update the questions according to the selected chapter
$("body").on("change", "#Qpartial", function () {
var CatgId = $("#Qpartial").val();
$.ajax({
url: 'PartialView1',
data: { id: CatgId },
type: 'Get',
success: function (response) {
$("#Questionpartial").html(response)
console.log(CatgId);
}
})
});
</script>
#model IEnumerable<EPQProjectTrackingTool.Models.QuestionCategory>
<table #*border="1" class="table table-striped"*#>
<thead>
<tr>
<th>
#Html.DisplayNameFor(model => model.Question)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#*#Html.DisplayFor(modelItem => item.Question)*#
<input name="AreChecked" type="checkbox" checked="checked" value="#item.Question" /> #item.Question<br />
</td>
</tr>
}
</tbody>
</table>
there is one more file which is same HTML but only showing the full list as show in the above pic.
and the stored procedure in sql is:
CREATE procedure [dbo].[spGetAllMapping_Question_Category]
as
Begin
SELECT
mpq.UID,mpq.Question_ID_FK_1,eq.Question ,mpq. Cat_ID_FK_2, ec.Description, mpq.Valid_From,
mpq.Valid_To,c.Chapter_Name
FROM
EPQ2.Category ec,EPQ2.Questions eq, EPQ2.Mapping_Question_Category mpq, EPQ2.Chapter c
WHERE
mpq.Question_ID_FK_1 = eq.Question_ID_PK
and mpq.Cat_ID_FK_2 = ec.Cat_ID_PK
and c.Chapter_Id = eq.Chapter_Id
order by c.Ch_Sequence_ID , eq.Sequence_ID,ec.Cat_Name
End
CREATE procedure [dbo].[spGetQuestionsPerCategory](#category_Id int)
as
Begin
SELECT
eq.Question
FROM
EPQ2.Questions eq, EPQ2.Mapping_Question_Category mpq
WHERE
mpq.Question_ID_FK_1 = eq.Question_ID_PK
and mpq.Cat_ID_FK_2 = #category_Id;
End
the summary or similar example would be to select all the rows from a table and then put a filter which shows all the rows again but maybe make bold the filtered one and rest of them keep as it is.
Can't comment as I lack the reputation, so some liberties in assumption are gonna be taken and I can edit later if I made the wrong assumptions.
From my understanding you want a list of questions shown to the user, with all the questions matching the category from the dropdown to be selected.
If it's possible to change the return type from the stored procedure you could just have a sql function that returns question and whether it should be checked.
CREATE procedure [dbo].[spGetAllMapping_Question_Category](#category_Id int)
as
Begin
SELECT
mpq.UID,mpq.Question_ID_FK_1,eq.Question ,mpq.Cat_ID_FK_2, ec.Description, mpq.Valid_From,
mpq.Valid_To,c.Chapter_Name,
CASE
WHEN mpq.Cat_ID_FK_2 = #category_Id THEN 1
ELSE 0
END as 'Checked'
FROM
EPQ2.Category ec,EPQ2.Questions eq, EPQ2.Mapping_Question_Category mpq, EPQ2.Chapter c
WHERE
mpq.Question_ID_FK_1 = eq.Question_ID_PK
and mpq.Cat_ID_FK_2 = ec.Cat_ID_PK
and c.Chapter_Id = eq.Chapter_Id
order by c.Ch_Sequence_ID , eq.Sequence_ID,ec.Cat_Name
End
Bit value can be set to boolean in the questionCategoryDataAccess. I know some readers have a GetBoolean function or you can use some ternary operators.
Then you can set up an checkbox using this boolean.
#foreach (var item in Model)
{
<tr>
<td>
#if (item.Checked == true)
{
<input name="AreChecked" type="checkbox" checked="checked" value="#item.Question" /> #item.Question<br />
}
else
{
<input name="AreChecked" type="checkbox" value="#item.Question" /> #item.Question<br />
}
</td>
</tr>
}
Might be easier to use Html helpers but I followed the standard you had.
Hope this helps and if I made some wrong assumptions let me know and I'll do my best to help get the right solution.

Deleting a row from a table and passing a Boolean flag to API, without deleting the actual object

I'm doing a spa with AngularJS and consuming an api with mvvm in C #.
I can delete a line after clicking the delete button, but on the server I want only to change a boolean flag to true by keeping the data in Sql Server.
I've tried other ways, and I was even deleting the object through Postman, but I do not want to delete but only change a Boolean property to the record that no longer exists in my view table.
I'll leave my code so it can be better understood.
Any help is welcome.
I have tried to pass the id and the object in api controller, similar to http.put, because I want to change a Boolean property, so I wanted to keep the id, name, last name, email and isdelete that after the click, changes to delete the line in the view becomes true in the database.
<tbody>
<tr ng-repeat="register in registers">
<td style="display:none">{{register.UserId}}</td>
<td>{{register.Name}}</td>
<td>{{register.LastName}}</td>
<td><input type="checkbox" ng-model="register.IsActive" disabled /></td>
<td>{{register.Email}}</td>
<td>
<a ng-click="editRegister($event, register)" class="glyphicon glyphicon-edit"></a>
</td>
<td>
</td>
</tr>
</tbody>
My controller.js:
$scope.deleteRegister = function (register) {
var index = -1;
var listOfRegister = eval($scope.registers);
for (var i = 0; i < listOfRegister.length; i++) {
if (listOfRegister[i].register === register) {
index = i;
break;
}
}
if (index === -1) {
alert("Something gone wrong");
}
$scope.registers.splice(index, 1);
$http({
method: 'Delete',
url: 'http://localhost:51734/api/UserAPI/',
}).then(function (res) {
alert('Exc');
$scope.GetAllRegisters();
})
};
And My controller.api:
[HttpPut]
[Route("api/UserAPI")]
public HttpResponseMessage Delete(UserViewModel uservm)
{
try
{
var registerDeleted = ctx.User.Find(uservm.UserId);
uservm.IsDelete = false;
uservm.IsActive = true;
if (registerDeleted != null)
{
uservm.IsActive = false;
uservm.IsDelete = true;
registerDeleted.Name = uservm.Name;
registerDeleted.LastName = uservm.LastName;
registerDeleted.IsActive = uservm.IsActive;
registerDeleted.Email = uservm.Email;
registerDeleted.IsDelete = uservm.IsDelete;
ctx.Entry(registerDeleted).State = System.Data.Entity.EntityState.Modified;
ctx.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, "User not found");
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
Thanks!
what you are talking about is soft deleting rows of data.
in the database add a new boolean field called Status or IsDeleted or whatever you fancy, with a default value of false.
When you hit the Delete button on the UI, issue a request to simply update that flag from false to true
When you request the list of things to show on the UI, oly return those where the flag is set to false.
This is basically the whole idea behind soft deleting data. You only mark the data is deleted, you don't actually delete it.
If you want, later on you can add a process where you move the soft deleted data to an archived table, just to keep things tidy. Doing things this way has the added benefit that you can always undelete the data, should you need to. All you need really is to change the flag back to false and you're done, everything works and the data gets displayed.
One final point from me, if you all you want to do is soft delete an item, then all you need is to pass the id of the record. you don't need to worry about changes to other fields. You wouldn't normally update information AND delete it at the same time. So create a simple controller which responds to PUT, for example:
[HttpPut]
[Route("api/UserAPI")]
public HttpResponseMessage Delete(int userId)
{
var foundUser = ctx.User.Find(userId);
foundUser.IsDeleted = true;
ctx.SaveChanges();
}
of course, add all your validation, return codes, everything you need, but this is essentially all you need.
Don't forget to change the method which returns the active users, to ignore those where the IsDeleted flag is true.
If the API is the issue you are setting uservm.IsDelete = false; then setting registerDeleted.IsDelete = uservm.IsDelete; therefore it would always be false in the database.
If the template is the issue i would recommend getting the index from the data-ng-repeat, this would reduce the amount of code you have written and make it easier to see what is going on.
This will remove the row from the table:
<tr data-ng-repeat="(index, register) in registers">
<td>
</td>
</tr>
$scope.deleteRegister = function (index) {
$scope.registers.splice(index, 1);
}
Update from comment:
You need to retrieve the object then make the change, and save it.
var registerDeleted = ctx.User.Find(uservm.UserId);
if (registerDeleted != null)
{
registerDeleted.IsDelete = true;
registerDeleted.IsActive = false
ctx.SaveChanges();
}
Now I already know how to delete a record from my View.index, which is with splice in my controller.js
$scope.deleteRegister = function (index) {
$scope.registers.splice(index, 1);
$http({
method: 'PUT',
url: 'http://localhost:51734/api/UserAPI/',
}).then(function (res) {
alert('Exc');
$scope.GetAllRegisters();
})
};
But I can not get it to communicate with my method in my controller.api, I want to change only the flag isDelete = true, after clicking the delete button on my UI.
My controller.api
//DELETE: api/UserAPI/5
[HttpPut]
[Route("api/UserAPI")]
public HttpResponseMessage Delete(int userId)
{
try
{
var foundUser = ctx.User.Find(userId);
if (foundUser != null)
{
foundUser.IsDelete = true;
ctx.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
}
And my view.index
<tr ng-repeat="(index, register) in registers">
<td>
</td>
</tr>

Use a button to load more data from the server without refresh the page

My server load articles from a xml, and send them to my view.
I'd like to only send some articles instead, not all of them, and provide to the user a button to load more articles.
But how can I send these new data to my view without refreshing the page, is it possible to only update the Model?
public IActionResult Index()
{
List<Article> articles = new List<Article>();
XmlSerializer serializer = new XmlSerializer(typeof(List<Article>), new XmlRootAttribute("Articles"));
using (StreamReader reader = new StreamReader(HostingEnvironment.WebRootPath + #"/articles/articles.xml"))
{
articles = (List<Article>)serializer.Deserialize(reader);
}
return View(articles);
}
<div id="articles">
#foreach (Article art in Model)
{
var articleImage = "/images/articles/" + art.Image + ".jpg";
<article>
<div class="article_title_and_date">
<h2 class="article_title">#art.Title</h2>
<p class="article_date">#art.Date</p>
</div>
<img src="#Url.Content(articleImage)" alt="image">
<p>
#art.Text
</p>
</article>
}
</div>
You're going to need to implement some JavaScript to talk to your server via an API. Here's a basic example of getting some different data from a server on each click of the button.
var postNumber = 1;
document.getElementById('getNextPost').addEventListener('click', function() {
var currentPost = document.getElementById('currentPost');
var url = `https://jsonplaceholder.typicode.com/posts/${postNumber++}`;
fetch(url)
.then(response => response.json())
.then(json => currentPost.innerHTML = json.body)
})
<div id="currentPost">Some static content from the server</div>
<button id="getNextPost">Get Next Post</button>
This example uses a JSON endpoint; however, you can read values from an XML endpoint by using window.DOMParser inside the .then()
new window.DOMParser()).parseFromString(str, "text/xml")

File upload with jquery form plugin and mvc 3 is not filling in the target property with my partial view

I'm trying to setup a simple file/image upload for a web app I'm working on. To help, I'm using the jquery form plugin found here: http://jquery.malsup.com/form/
From the examples, it seems that you define where you want your return data to be placed by defining the "target" property.
So the problem is that instead of rendering the partial inside the defined 'target' location, my whole browser is 'posting back' and I get redirected to the individual partials page.
public PartialViewResult BackgroundImageWindow()
{
return PartialView();
}
BackgroundImageWindow.cshtml
<div class="divBGImageLoader">
<form id="FileUploadForm" action='#Url.Action("FileUpload", "SlideShow")' method="POST" enctype="multipart/form-data">
<input type="file" name="file" />
<input id="UploadFileButton" type="submit" value="Upload" />
</form>
<div id="BGImageTable">
#{Html.RenderAction("BackgroundImageWindowTable");}
</div>
</div>
Which goes here:
public PartialViewResult BackgroundImageWindowTable()
{
DirectoryInfo di = new DirectoryInfo(Server.MapPath("~/Content/uploads"));
List<FileInfo> files = di.GetFiles().ToList();
return PartialView(files); // returns a partial with a table of the uploaded files
}
javascript:
$("#UploadFileButton").live("submit", function(e){
e.preventDefault(); // <- doc file said it needed this
e.stopImmediatePropagation(); // <- uuh just in case
var ajaxSubmitOptions = {
target: $("#BGImageTable"),
beforeSubmit: function () {
//$("#loading").show();
},
success: function (data) {
//$("#loading").hide();
}
};
$(this).ajaxSubmit(ajaxSubmitOptions);
return false; //<- documentation said this was necessary to stop the 'post back'
});
FileUpload Part:
public ActionResult FileUpload(HttpPostedFileBase file)
{
// Verify that the user selected a file
if (file != null && file.ContentLength > 0)
{
// extract only the fielname
var fileName = Path.GetFileName(file.FileName);
// store the file inside ~/App_Data/uploads folder
var path = Path.Combine(Server.MapPath("~/Content/uploads"), fileName);
file.SaveAs(path);
}
// redirect back to the index action to show the form once again
return RedirectToAction("BackgroundImageWindowTable");
}
So like I said previously, this seems to be working except for the fact that the partial is being rendered and displayed like if it was a separate page.
You should connect to form submit event instead of UploadFileButton. Just like this
$("#FileUploadForm").live("submit", function(e){
// do your stuff here
}

Categories