How to insert an image into sql server database? - sql-server

As said, i'm trying to insert an image in a table, where the type of the field is Varbinary.
What i've done so far :
I've a form with many fields:
#using (Html.BeginForm("InsertProduct", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>PRODUCT</legend>
<div class="editor-label">
#Html.LabelFor(model => model.PRODUCT_ID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PRODUCT_ID)
#Html.ValidationMessageFor(model => model.PRODUCT_ID)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PRODUCT_NAME)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.PRODUCT_NAME)
#Html.ValidationMessageFor(model => model.PRODUCT_NAME)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.PRODUCT_IMAGE)
</div>
<div class="editor-field">
<input type="file" name="PRODUCT_IMAGE" id="PRODUCT_IMAGE" style="width: 100%;" />
</div>
<p>
<input type="submit" value="Create" class="btn btn-primary"/>
</p>
</fieldset>
}
And all these fields allow me to construct a PRODUCT object in my controller :
public ActionResult InsertProduct(PRODUCT ord)
{
MigrationEntities1 sent = new MigrationEntities1();
sent.PRODUCT.Add(ord);
sent.SaveChanges();
List<PRODUCT> Products = sent.PRODUCT.ToList();
return View("Products", Products);
}
But when i'm trying to upload the image (by clicking on Create button), i've the following :
entry is not a valid base64 string because it contains a character
that is not base 64
So first of all : is it the right way to deal with images and second, I think I need to do a pre-treatemant on my image to insert it : how to o that ?
Thanks !
Edit :
Thanks to answers received, seems to be good for insertion. But for displaying, I still have issues (only the "not found image" piture is displayed). I've try to do it two ways :
1.
<img src="LoadImage?id=#Model.product.PRODUCT_ID"/>
and in the controller
public Image LoadImage(int id)
{
String serviceAddress = ConfigurationManager.AppSettings["WCFADDRESS"];
DataServiceContext context = new DataServiceContext(new Uri(serviceAddress));
PRODUCT product = context.Execute<PRODUCT>(new Uri(serviceAddress + "prod_id?prod_id=" + id)).ToList().FirstOrDefault();
MemoryStream ms = new MemoryStream(product.PRODUCT_IMAGE);
Image img = Image.FromStream(ms);
return img;
}
And 2. :
#{
if (Model.product.PRODUCT_IMAGE != null)
{
WebImage wi = new WebImage(Model.product.PRODUCT_IMAGE);
wi.Resize(700, 700,true, true);
wi.Write();
}
}
But none of them are working. What am I doing wrong ?

1) Change your database table to have these columns:
1: ProductImage - varbinary(MAX)
2: ImageMimeType - varchar(50)
2) Change your action method like this:
public ActionResult InsertProduct(PRODUCT ord,
HttpPostedFileBase PRODUCT_IMAGE)
{
if (ModelState.IsValid)
{
MigrationEntities1 sent = new MigrationEntities1();
if (image != null)
{
ord.ProductImage= new byte[PRODUCT_IMAGE.ContentLength];
ord.ImageMimeType = PRODUCT_IMAGE.ContentType;
PRODUCT_IMAGE.InputStream.Read(ord.ProductImage, 0,
PRODUCT_IMAGE.ContentLength);
}
else
{
// Set the default image:
Image img = Image.FromFile(
Server.MapPath(Url.Content("~/Images/Icons/nopic.png")));
MemoryStream ms = new MemoryStream();
img.Save(ms, ImageFormat.Png); // change to other format
ms.Seek(0, SeekOrigin.Begin);
ord.ProductImage= new byte[ms.Length];
ord.ImageMimeType= "image/png";
ms.Read(ord.Pic, 0, (int)ms.Length);
}
try
{
sent.PRODUCT.Add(ord);
sent.SaveChanges();
ViewBag.HasError = "0";
ViewBag.DialogTitle = "Insert successful";
ViewBag.DialogText = "...";
}
catch
{
ViewBag.HasError = "1";
ViewBag.DialogTitle = "Server Error!";
ViewBag.DialogText = "...";
}
List<PRODUCT> Products = sent.PRODUCT.ToList();
return View("Products", Products);
}
return View(ord);
}
This action method is just for create. you need some works for edit and index too. If you have problem to doing them, tell me to add codes of them to the answer.
Update: How to show images:
One way to show stored images is as the following:
1) Add this action method to your controller:
[AllowAnonymous]
public FileContentResult GetProductPic(int id)
{
PRODUCT p = db.PRODUCTS.FirstOrDefault(n => n.ID == id);
if (p != null)
{
return File(p.ProductImage, p.ImageMimeType);
}
else
{
return null;
}
}
2) Add a <img> tag in the #foreach(...) structure of your view (or wherever you want) like this:
<img width="100" height="100" src="#Url.Action("GetProductPic", "Products", routeValues: new { id = item.ID })" />

Change the Image type on the sql sever to Byte[] and use something like this. This is how I have stored images in the past.
http://www.codeproject.com/Articles/15460/C-Image-to-Byte-Array-and-Byte-Array-to-Image-Conv
If not, you can always just store the image locally and pass the image location through a string into the SQL data base, this method works well and is quick to set up.

So, here are the modifications to do :
To insert data in the database :
[HttpPost]
public ActionResult InsertProduct(PRODUCT ord, HttpPostedFileBase image)
{
MigrationEntities1 sent = new MigrationEntities1();
if (image != null)
{
ord.PRODUCT_IMAGE = new byte[image.ContentLength];
image.InputStream.Read(ord.PRODUCT_IMAGE, 0, image.ContentLength);
}
sent.PRODUCT.Add(ord);
sent.SaveChanges();
List Products = sent.PRODUCT.ToList();
return View("Products", Products);
}
Note: this is the "light" way, for something that is more complete, have a look to Amin answer.
For displaying :
In the view
<img src="LoadImage?id=#Model.product.PRODUCT_ID"/>
And in the controller :
public FileContentResult LoadImage(int id)
{
String serviceAddress = ConfigurationManager.AppSettings["WCFADDRESS"];
DataServiceContext context = new DataServiceContext(new Uri(serviceAddress));
PRODUCT product = context.Execute<PRODUCT>(new Uri(serviceAddress + "prod_id?prod_id=" + id)).ToList().FirstOrDefault();
return new FileContentResult(product.PRODUCT_IMAGE, "image/jpeg");
}
And everything is ok now, thanks !

Related

Want to filter by a child Entity in 2scx template

I have a list Entity called Awards which has a Name (string) and YearGiven (Entity) as its fields.
I want to show all awards grouped by Year.
ie
2017
---Bob
---Sue
2016
---Fred
2015
etc
Here is my template:
#using ToSic.SexyContent
#functions
{
// variable which will contain the sorted categories
IEnumerable<dynamic> sortedCategories;
// Prepare the data - get all categories through the pipeline
public override void CustomizeData()
{
// get all categories of these questions, then get the distinct entities
// this could all be done on 1 line, but it would be harder for people who don't know LINQ yet
var awardsInThisModule = AsDynamic(App.Data["Awards"].List);
var categoriesUsed = awardsInThisModule.SelectMany(q => ((List<DynamicEntity>)q.YearGiven));
var distinctCategories = categoriesUsed.Select(AsEntity).Distinct(); // Distinct only works reliably when cast as entity
sortedCategories = AsDynamic(distinctCategories).OrderBy(q => q.Year);
}
}
<link rel="stylesheet" href="#App.Path/assets/awards.css" data-enableoptimizations="true" />
#foreach (var cat in sortedCategories)
{
<h3> #cat.Year</h3>
foreach (var q in AsDynamic(App.Data["Awards"].List).Where(t => t.Name == "Bob").OrderBy(q => q.Name))
{
//this works fine and puts Bob against each year
<h2>#q.Name</h2>
}
foreach (var q in AsDynamic(App.Data["Awards"].List).Where(t => t.Year.Select(a => AsDynamic(a).Year) == "2017"))
{
//this is what I actually want to do and fails
<h2>#q.Name</h2>
}
<br />
}
I started by changing the Where clause to t.YearGiven == 2016 but that gives an error "Operator '==' cannot be applied to operands of type 'System.Collections.Generic.List' and 'int' a" - I assume because YearGiven is an Entity and so is actually a List<>.
So then I changed to the next foreach in the code and got this error:-
"Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type."
I can't find any template example that does what I'm trying to do and nothing I do works.
N.B. I've hardcoded '2017' in there for now to keep things simple but it will obviously be doing each Year found in the outer loop.
Here is a simple example with a similar schema if you want to adapt it. I am basically using a variable (currCat) to keep track and handle the 'on-change of category'. Hopefully you can ignore all the expando/collapse stuff. Here is what the final looks like:
http://www.blackandco.com/Vendor-Linecard
<div id="vendor-list" role="tablist" class="small">
#{
int currCat = 0;
int firstCo = 851; // Abrasives
foreach (var aCat in AsDynamic(App.Data["CompanyCategories"])
.Where(c => c.CategoryActiveYN == true)
.OrderBy(c => c.CategoryName)
)
{
currCat = aCat.EntityId;
<div class="card">
<div class="card-header" role="tab" id="#string.Format("{0}{1}", "heading", #currCat)">
<h5 class="mb-0#((currCat == firstCo) ? "" : " collapsed")" data-toggle="collapse" href="#string.Format("{0}{1}", "#collapse", #currCat)"
aria-expanded="#((currCat == firstCo) ? "true" : "false")" aria-controls="#string.Format("{0}{1}", "collapse", #currCat)">
#aCat.CategoryName
</h5>
</div>
<div id="#string.Format("{0}{1}", "collapse", #currCat)" class="collapse#((currCat==firstCo) ? " show" : "")" role="tabpanel" aria-labelledby="#string.Format("{0}{1}", "heading", #currCat)" data-parent="#accordion" aria-expanded="#((currCat==firstCo) ? "true" : "false")">
<div class="card-body">
<ul>
#foreach (var vComp in AsDynamic(App.Data["Company"])
.Where(v => v.CompanyActiveYN && v.IncludeOnVendorCards)
.OrderBy(v => v.CompanyName)
)
{
foreach (var vCat in vComp.CompanyCategory)
{
if (vCat.EntityId == currCat)
{
<li>#vComp.CompanyName<span></li>
}
}
}
</ul>
</div>
</div>
</div>
}
}
</div>

Episerver create page programmatically

I am using this code
var parent = ContentReference.StartPage;
IContentRepository contentRepository = EPiServer.ServiceLocation.ServiceLocator.Current.GetInstance<IContentRepository>();
PageData myPage = contentRepository.GetDefault<LoginPage>(parent);
myPage.PageName = "My new page";
var page = contentRepository.GetChildren<LoginPage>(parent).FirstOrDefault(name => name.Name == myPage.Name);
if (page == null)
contentRepository.Save(myPage, EPiServer.DataAccess.SaveAction.Publish);
to create a page programatically. The thing is I am not sure where to put this code?
I don't want to show LoginPage which is page type to show in the list in the admin/edit panel as I want to create only one page under that page type. Maybe there is another way where I can just create a stand alone page and don't have to create the page type or maybe use an already made page type.
This is the code for my page type
[ContentType(DisplayName = "Custom Login Page", GUID = "c0d358c3-4789-4e53-bef3-6ce20efecaeb", Description = "")]
public class LoginPage : StandardPage
{
/*
[CultureSpecific]
[Display(
Name = "Main body",
Description = "The main body will be shown in the main content area of the page, using the XHTML-editor you can insert for example text, images and tables.",
GroupName = SystemTabNames.Content,
Order = 1)]
public virtual XhtmlString MainBody { get; set; }
*/
}
Then I am creating a model like this
public class LoginModel : PageViewModel<LoginPage>
{
public LoginFormPostbackData LoginPostbackData { get; set; } = new LoginFormPostbackData();
public LoginModel(LoginPage currentPage)
: base(currentPage)
{
}
public string Message { get; set; }
}
public class LoginFormPostbackData
{
public string Username { get; set; }
public string Password { get; set; }
public bool RememberMe { get; set; }
public string ReturnUrl { get; set; }
}
And my controller looks like this
public ActionResult Index(LoginPage currentPage, [FromUri]string ReturnUrl)
{
var model = new LoginModel(currentPage);
model.LoginPostbackData.ReturnUrl = ReturnUrl;
return View(model);
}
Do you think there is another way to do it? I will also show my login view
#using EPiServer.Globalization
#model LoginModel
<h1 #Html.EditAttributes(x =>
x.CurrentPage.PageName)>#Model.CurrentPage.PageName</h1>
<p class="introduction" #Html.EditAttributes(x =>
x.CurrentPage.MetaDescription)>#Model.CurrentPage.MetaDescription</p>
<div class="row">
<div class="span8 clearfix" #Html.EditAttributes(x =>
x.CurrentPage.MainBody)>
#Html.DisplayFor(m => m.CurrentPage.MainBody)
</div>
#if (!User.Identity.IsAuthenticated &&
!User.IsInRole("rystadEnergyCustomer"))
{
<div class="row">
#using (Html.BeginForm("Post", null, new { language = ContentLanguage.PreferredCulture.Name }))
{
<div class="logo"></div>
#Html.AntiForgeryToken()
<h2 class="form-signin-heading">Log in</h2>
#Html.LabelFor(m => m.LoginPostbackData.Username, new { #class = "sr-only" })
#Html.TextBoxFor(m => m.LoginPostbackData.Username, new { #class = "form-control", autofocus = "autofocus" })
#Html.LabelFor(m => m.LoginPostbackData.Password, new { #class = "sr-only" })
#Html.PasswordFor(m => m.LoginPostbackData.Password, new { #class = "form-control" })
<div class="checkbox">
<label>
#Html.CheckBoxFor(m => m.LoginPostbackData.RememberMe)
#Html.DisplayNameFor(m => m.LoginPostbackData.RememberMe)
</label>
</div>
#Html.HiddenFor(m => m.LoginPostbackData.ReturnUrl, "/login-customers")
<input type="submit" value="Log in" class="btn btn-lg btn-primary btn-block" />
}
#Html.DisplayFor(m => m.Message)
</div>
}
else
{
<span>Welcome #User.Identity.Name</span>
#Html.ActionLink("Logout", "Logout", "LoginPage", null, null);
}
I think you're misunderstanding some of the Episerver concepts.
If you don't want it to be a page in Episerver, you shouldn't use PageController, page types, or templates. Instead, just use a standard controller and view to create your login page.
Otherwise, you do have to create a page of type LoginPage, which will be visible in the page tree. No need to create it programmatically, you can just add the page manually and then hide the LoginPage type from edit mode to avoid editors creating additional login pages.

Vue.js: Manipulate Array and post form with new data

In my Vue.js application I want to post form data to my Node.js/MongoDB Backend.
This is my source code: https://github.com/markusdanek/t2w-vue/blob/master/src/components/backend/JobEdit.vue
JSON for my job entry: http://t2w-api.herokuapp.com/jobs/591c09a55ba85d0400e5eb61
Relevant code for my question:
HTML:
<div class="row">
<input type='text'
:name="'qual'+index"
v-model="qualifications[index]">
<button #click.prevent="removeQualifiaction(index)">X</button>
</div>
Methods:
onChange(value, $event){
if (!this.job.xmlOnline)
this.job.xmlOnline = []
const index = this.job.xmlOnline.findIndex(v => v == value)
const checked = $event.target.checked
if (checked && index < 0)
this.job.xmlOnline.push(value)
if (!checked && index >= 0)
this.job.xmlOnline.splice(index, 1)
}
removeQualifiaction() {
this.qualifications.splice(this.qualifications.index, 1);
}
Sending the form data with submit button on form end:
editJob() {
let job = Object.assign({}, this.job);
job.qualifications = this.qualifications;
job.responsibility = this.responsibility;
this.$http.post('https://t2w-api.herokuapp.com/jobs/' + this.$route.params.id, job).then(response => {
console.log(response);
}, response => {
console.log(response);
});
}
My problems now:
When I edit a "Job", I have a list of "qualification items", that are input fields in my form.
Clicking the "delete" button results that the first input gets deleted, not the one I am clicking. Done with #thanksd answer.
How do I add a button and method to add a new input field and to append it to my job.qualifications?
In my JobAdd.vue implemented, to add a new entry to job.qualifications, like this:
<a #click.prevent="addQualification">+</a>
addQualification() {
this.qualification.push({ text: '' });
}
addJob() {
let job = Object.assign({}, this.job);
job.qualifications = this.qualification.map(q => q.text);
this.$http.post('https://t2w-api.herokuapp.com/jobs/', job).then(response => {....
Full source for my JobAdd.vue: https://github.com/markusdanek/t2w-vue/blob/master/src/components/backend/JobAdd.vue
this.qualification.push({ text: '' }); doesnt work obviously not in my JobEdit.vue when there are already strings in my job.qualifications.
Change your removeQualifiaction method to use the index being passed in:
removeQualifiaction(index) {
this.qualifications.splice(index, 1);
}

Having some problems saving/displaying an image in MVC 4

I have searched around for a while now with no joy. I am trying to save an image to my SQL db as a byte array, then I am trying to display it later. The display part is not working. I don't know if it's a problem with the save or the display. The save appears to be working ok, I can see 'Binary Data' in my SQL table. Any suggestions?
What's happening is that I get a broken image icon on my page. Even if I manually goto the URL e.g. .../Treatments/LoadImage/14 it's broken.
Model contains this in my table definition:
public byte[] Photo { get; set; }
Create View:
<div class="editor-label">
#Html.LabelFor(model => model.Photo)
</div>
<div class="editor-field">
<input type="file" name="photo" />
</div>
Create Controller:
[HttpPost]
public ActionResult Create([Bind(Exclude = "Photo")]Treatment treatment)
{
if (ModelState.IsValid)
{
treatment.Photo = GetByteArrayFromFile();
treatment.WebOrder = db.Treatments.Count();
db.Treatments.Add(treatment);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.TreatmentTypeId = new SelectList(db.TreatmentTypes, "Id", "Name", treatment.TreatmentTypeId);
return View(treatment);
}
private byte[] GetByteArrayFromFile() {
int fileLength = Request.Files["photo"].ContentLength;
byte[] byteArray = new byte[fileLength];
return byteArray;
}
Display View:
<div class="display-label">
#Html.DisplayNameFor(model => model.Photo)
</div>
<div class="display-field">
<img src="#Url.Action("LoadImage", new { Id = Model.Id })" />
</div>
LoadImage Controller:
public ActionResult LoadImage(int Id) {
byte[] bytes = db.Treatments.Find(Id).Photo;
return File(bytes, "image/jpeg");
}
I have added:
#using (Html.BeginForm("Create", "Treatments", FormMethod.Post, new { enctype = "multipart/form-data" })) {
// View Code Omitted
}
to my Create view.
Is there something elementary wrong with my code? Any suggestions? Thanks.
It was a problem with my GetByteArrayFromFile Method. My original method above returned an array of zeros only. Using this, I fixed my issue:
private byte[] GetByteArrayFromFile() {
WebImage image = WebImage.GetImageFromRequest();
byte[] byteArray = image.GetBytes();
return byteArray;
}

Image saved in database cannot be updated

I have been googling for a solution to my problem for two days without any luck. Can any stars in MVC3 .NET help?
I am trying to build an .NET MVC3 application to update images saved in an database.
Here is the action method
[HttpPost]
public ActionResult Edit(myImage img, HttpPostedFileBase imageFile)
{
//var img = (from imga in db.myImages
// where imga.imageID == id
// select imga).First();
if (ModelState.IsValid)
{
if (img != null)
{
img.imageType = imageFile.ContentType;
img.Data = new byte[imageFile.ContentLength];
imageFile.InputStream.Read(img.Data, 0, imageFile.ContentLength);
}
// save the product
UpdateModel(img);
db.SubmitChanges();
return RedirectToAction("Index");
}
else
{
// there is something wrong with the data values
return View(img);
}
}
Here is the view
#model JackLing.Models.myImage
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm("Edit", "Image",FormMethod.Post, new { enctype = "multipart/form-data" })){
#Html.ValidationSummary(true)
<fieldset>
<legend>myImage</legend>
#Html.EditorForModel();
<div class="editor-label">Image</div>
<div class="editor-field">
#if (Model.Data != null)
{
<img src="#Url.Action("show", new { id = Model.imageID })" height="150" width="150" />
}
else {
#:None
}
</div>
<p>
<span>Choose a new file</span> <input type="file" name="imgFile"/>
</p>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
When I run the application it throws an error saying "Object reference not set to an instance of an object."
Any suggestions on how to fix the problems will be appriciated! By the way, the create and details method are all working. I think it has to do with data binding, but I'm not sure... I have no clue how to fix it.
Finally fixed the problem based on the advice from Eulerfx
here is the working action.
[HttpPost]
public ActionResult Edit(myImage img, HttpPostedFileBase imageFile)
{
myImage imgToSave = (from imga in db.myImages
where imga.imageID == img.imageID
select imga).First();
if (ModelState.IsValid)
{
if (img != null)
{
imgToSave.imageType = imageFile.ContentType;
var binaryReader = new BinaryReader(imageFile.InputStream);
imgToSave.Data = binaryReader.ReadBytes(imageFile.ContentLength);
binaryReader.Close();
}
TryUpdateModel(imgToSave);
db.SubmitChanges();
return RedirectToAction("Index");
}
else
{
// there is something wrong with the data values
return View(img);
}
}
The problem may be that the name of the file input field in the HTML is imgeFile and the name of the file parameter in MVC is imageFile. Ensure that the input field name and the action method parameter names match.

Resources