Convert and save MvcHtmlString to Image or PDF - asp.net-mvc-4

I am currently working on an application that basically builds an MvcHtmlString by mapping a HtmlTemplate with some data dynamically.
What I want to be able to do is to Convert and save this MvcHtmlString as an Image/ PDF to my local disk.
Here is my function that produces the MvcHtmlString after mapping:
public static MvcHtmlString Map(this IDictionary<string, object> row, string htmlTemplate)
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(htmlTemplate);
foreach (var key in row.Keys)
{
var elements = htmlDoc.DocumentNode.SelectSingleNode("//body")
.Descendants()
.Where(d => d.Attributes
.Any(a => a.Name == "class" && a.Value == key));
if (elements != null && elements.Count() > 0)
{
foreach (var element in elements)
{
object attributeValue = null;
row.TryGetValue(key, out attributeValue);
if (element.HasChildNodes)
{
// We only get the first img element within the element
// as we dont expect there to be more than one <img> tag
// within a parent element
var imgChildNode = element.Descendants("img").FirstOrDefault();
if (imgChildNode != null)
{
imgChildNode.SetAttributeValue("src", attributeValue.ToString());
}
else
{
element.InnerHtml = string.Empty;
element.InnerHtml = attributeValue.ToString();
}
}
else
{
element.InnerHtml = string.Empty;
element.InnerHtml = attributeValue.ToString();
}
}
}
}
var sw = new StringWriter();
htmlDoc.Save(new StringWriter(sw.GetStringBuilder()));
var htmlString = MvcHtmlString.Create(sw.ToString());
return htmlString;
}
And then I am using this function to save an image (But this just renders a black block)
private void SaveImageFromHtml(MvcHtmlString html)
{
var decodedHtml = html.ToHtmlString();
Bitmap m_Bitmap = new Bitmap(600, 800);
PointF point = new PointF(0, 0);
SizeF maxSize = new System.Drawing.SizeF(600, 800);
HtmlRenderer.HtmlRender.Render(Graphics.FromImage(m_Bitmap), decodedHtml,
point, maxSize);
m_Bitmap.Save(#"D:\Test.png", ImageFormat.Png);
}
Any help will be appreciated!

Found the reason. The HtmlRenderer library that I was using didnt support "float" in css markup and hence the output image was messed up

Related

Download the file as a zip in ASP.NET Core

I am designing an educational site. When the user downloads a training course, I want this download (training course) to be done in the form of compression (zipper), please give a solution
My code:
public Tuple<byte[],string,string> DownloadFile(long episodeId)
{
var episode=_context.CourseEpisodes.Find(episodeId);
string filepath = Path.Combine(Directory.GetCurrentDirectory(),
"wwwroot/courseFiles",
episode.FileName);
string fileName = episode.FileName;
if(episode.IsFree)
{
byte[] file = System.IO.File.ReadAllBytes(filepath);
return Tuple.Create(file, "application/force-download",fileName);
}
if(_httpContextAccessor.HttpContext.User.Identity.IsAuthenticated)
{
if(IsuserIncorse(_httpContextAccessor.HttpContext.User.Identity.Name,
episode.CourseId))
{
byte[] file = System.IO.File.ReadAllBytes(filepath);
return Tuple.Create(file, "application/force-download", fileName);
}
}
return null;
}
I write a demo to show how to download zip file from .net core:
First , Add NuGet package SharpZipLib , create an Image Folder in wwwroot and put some picture in it.
controller
public class HomeController : Controller
{
private IHostingEnvironment _IHosting;
public HomeController(IHostingEnvironment IHosting)
{
_IHosting = IHosting;
}
public IActionResult Index()
{
return View();
}
public FileResult DownLoadZip()
{
var webRoot = _IHosting.WebRootPath;
var fileName = "MyZip.zip";
var tempOutput = webRoot + "/Images/" + fileName;
using (ZipOutputStream IzipOutputStream = new ZipOutputStream(System.IO.File.Create(tempOutput)))
{
IzipOutputStream.SetLevel(9);
byte[] buffer = new byte[4096];
var imageList = new List<string>();
imageList.Add(webRoot + "/Images/1202.png");
imageList.Add(webRoot + "/Images/1data.png");
imageList.Add(webRoot + "/Images/aaa.png");
for (int i = 0; i < imageList.Count; i++)
{
ZipEntry entry = new ZipEntry(Path.GetFileName(imageList[i]));
entry.DateTime= DateTime.Now;
entry.IsUnicodeText = true;
IzipOutputStream.PutNextEntry(entry);
using (FileStream oFileStream = System.IO.File.OpenRead(imageList[i]))
{
int sourceBytes;
do
{
sourceBytes = oFileStream.Read(buffer, 0, buffer.Length);
IzipOutputStream.Write(buffer, 0, sourceBytes);
}while (sourceBytes > 0);
}
}
IzipOutputStream.Finish();
IzipOutputStream.Flush();
IzipOutputStream.Close();
}
byte[] finalResult = System.IO.File.ReadAllBytes(tempOutput);
if (System.IO.File.Exists(tempOutput)) {
System.IO.File.Delete(tempOutput);
}
if (finalResult == null || !finalResult.Any()) {
throw new Exception(String.Format("Nothing found"));
}
return File(finalResult, "application/zip", fileName);
}
}
when I click the downloadZip ,it will download a .zip file
The simple example that follows illustrates the use of the static ZipFile.CreateFromDirectory method which, despite the fact that it is in the System.IO.Compression namespace , actually resides in the System.IO.Compression.FileSystem assembly, so you need to add a reference to that in your controller.
[HttpPost]
public FileResult Download()
{
List<string> files = new List<string> { "filepath1", "filepath2" };
var archive = Server.MapPath("~/archive.zip");
var temp = Server.MapPath("~/temp");
// clear any existing archive
if (System.IO.File.Exists(archive))
{
System.IO.File.Delete(archive);
}
// empty the temp folder
Directory.EnumerateFiles(temp).ToList().ForEach(f => System.IO.File.Delete(f));
// copy the selected files to the temp folder
files.ForEach(f => System.IO.File.Copy(f, Path.Combine(temp, Path.GetFileName(f))));
// create a new archive
ZipFile.CreateFromDirectory(temp, archive);
return File(archive, "application/zip", "archive.zip");
}
Answer from Source - MikesDotNetting

Resizing and creating image thumbnail in ASP.NET-Core 2.2

I am trying to create a thumbnail image in asp.net-core 2.2 application but I keep getting the above error whenever it gets to the point of creating the thumbnail.
The main image creates and stores fine but it is not able to create the thumbnail. Please I will appreciate any guide to resolve the error
Here are my methods for storing the uploaded image. This one works as expected
using LazZiya.ImageResize;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace eSchool.Models.Utilities
{
public class FileUploadHelper
{
private readonly IHostingEnvironment host;
public FileUploadHelper(IHostingEnvironment _host)
{
host = _host;
}
public async Task<string> SaveFileAsync(IFormFile file, string pathToUplaod)
{
string webroot=host.WebRootPath;
string DesiredDirectoryLocation = Path.Combine(webroot,pathToUplaod);
if(!Directory.Exists(DesiredDirectoryLocation))
{
Directory.CreateDirectory(DesiredDirectoryLocation);
}
string imageUrl = string.Empty;
var filename = Path.GetRandomFileName();
var newfilename = CreateUniqueFileName(file);
string pathwithfileName = DesiredDirectoryLocation + "/" + newfilename;
using (var fileStream = new FileStream(pathwithfileName, FileMode.Create))
{
await file.CopyToAsync(fileStream);
}
imageUrl = newfilename;
return imageUrl;
}
I have tried two different methods to create the thumbnail but either of them gives the above error
Here are the two methods.
The first one is this:
public string CreateThumbImage(IFormFile uploadedFile, string desiredThumbPath,string desiredThumbFilename, int desiredThumbWidth, int desiredThumbHeight)
{
try
{
Stream filestream = uploadedFile.OpenReadStream();
Image thumbnailStream = Image.FromStream(filestream);
Image thumbnailImage = thumbnailStream.GetThumbnailImage(desiredThumbWidth, desiredThumbHeight, () => false, IntPtr.Zero);
string webroot = host.WebRootPath;
string DesiredDirectoryLocation = Path.Combine(webroot, desiredThumbPath);
if (!Directory.Exists(DesiredDirectoryLocation))
{
Directory.CreateDirectory(DesiredDirectoryLocation);
}
string thumbFullPathName = desiredThumbPath + "/" + desiredThumbFilename;
thumbnailImage.Save(thumbFullPathName);
return thumbFullPathName;
}
catch
{
throw;
}
}
And the second one is this:
public void ResizeImage(IFormFile uploadedFile, string desiredThumbPath, int desiredWidth=0, int desiredHeight=0)
{
if (uploadedFile.Length > 0)
{
using (var stream = uploadedFile.OpenReadStream())
{
var uploadedImage = System.Drawing.Image.FromStream(stream);
//decide how to scale dimensions
if (desiredHeight == 0 && desiredWidth > 0)
{
var img = ImageResize.ScaleByWidth(uploadedImage, desiredWidth); // returns System.Drawing.Image file
img.SaveAs(desiredThumbPath);
}
else if(desiredWidth == 0 && desiredHeight > 0)
{
var img = ImageResize.ScaleByHeight(uploadedImage, desiredHeight); // returns System.Drawing.Image file
img.SaveAs(desiredThumbPath);
}
else
{
var img = ImageResize.Scale(uploadedImage, desiredWidth,desiredHeight); // returns System.Drawing.Image file
img.SaveAs(desiredThumbPath);
}
}
}
return;
}
And this is where I call the methods:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
FileUploadHelper uploadHelper = new FileUploadHelper(_host);
if (EmailValidation.EmailExists(model.EmailAddress,_context))
{
ModelState.AddModelError("EmailAddress", "This email address is already registered with us.");
}
if (model.Photo != null)
{
string[] extensions = new string[] { ".jpeg",".jpg", ".gif", ".png" };
///Validate the type of the image file being uploaded
ResponseMsg fileTypeValidated = uploadHelper.ValidateFileExtension(model.Photo, extensions);
if (!fileTypeValidated.ResponseStatus)
{
ModelState.AddModelError("Photo", fileTypeValidated.ResponseDescription);
}
///Validate the size of the image file being uploaded
ResponseMsg fileSizeValidated = uploadHelper.ValidateFilesize(model.Photo, 1);
if (!fileSizeValidated.ResponseStatus)
{
ModelState.AddModelError("Photo", fileSizeValidated.ResponseDescription);
}
}
if (ModelState.IsValid)
{
try
{
Instructor instructor = new Instructor
{
Surname = model.Surname,
OtherNames = model.Othernames,
Email = model.EmailAddress,
UserName = model.EmailAddress,
PhoneNumber = model.PhoneNumber,
Gender = model.Gender,
StateId = model.ResidenceState,
LgaId = model.ResidenceLga,
DateOfBirth = model.DateOfBirth,
TimeRegistered = DateTime.Now
};
var photo = await uploadHelper.SaveFileAsync(model.Photo,"images/instructors");
//Create image thumbnail for the instructor photo
var photo_thumbnail = "images/instructors/thumbs/" + photo;
uploadHelper.CreateThumbImage(model.Photo, "images/instructors/thumbs/", photo, 100, 100);...
Please help me if you can point out where I am missing the right path or a better way to handle image thumbnail creation in ASP.NET-Core 2.* to fix the error.
Regards
The error came from the path of the thumbnail. The path given in the ResizeImage method does not indicate the filename of the thumbnail image. That is where the generic GDI+ error was coming from.
So using the ResizeImage method works when the path of the resized image (including the image filename) is correctly passed to the SaveAs method. Here is the working method:
public void ResizeImage(IFormFile uploadedFile, string desiredThumbPath, int desiredWidth=0, int desiredHeight=0)
{
string webroot = host.WebRootPath;
if (uploadedFile.Length > 0)
{
using (var stream = uploadedFile.OpenReadStream())
{
var uploadedImage = System.Drawing.Image.FromStream(stream);
//decide how to scale dimensions
if (desiredHeight == 0 && desiredWidth > 0)
{
var img = ImageResize.ScaleByWidth(uploadedImage, desiredWidth); // returns System.Drawing.Image file
img.SaveAs(Path.Combine(webroot,desiredThumbPath));
}
else if(desiredWidth == 0 && desiredHeight > 0)
{
var img = ImageResize.ScaleByHeight(uploadedImage, desiredHeight); // returns System.Drawing.Image file
img.SaveAs(Path.Combine(webroot,desiredThumbPath));
}
else
{
var img = ImageResize.Scale(uploadedImage, desiredWidth,desiredHeight); // returns System.Drawing.Image file
img.SaveAs(Path.Combine(webroot,desiredThumbPath));
}
}
}
return;
}
And the implementation is as follows:
//Create image thumbnail for the instructor photo
var photo_thumbnail = "images/instructors/thumbs/" + photo;
uploadHelper.ResizeImage(model.Photo, photo_thumbnail, 100);
Remember to include the following using statement in the parent class of uploadHelper that houses the ResizeImage method as follows:
using LazZiya.ImageResize;
Meanwhile, LazZiya.ImageResize is nugget package for managing image resizing in asp.net-core 2.1
See the github link for it Github link for LazZiya image nugget
So, that solves your image resizing problem in asp.net-core
Regards
Once file uploaded/saved to server. You can use following ASP.NET core middleware to serve image thumbnails.
https://github.com/osprakash/ImageThumbnail-aspnetcore
Configure middleware in startup.cs and pass thumbnail size like below :
Disclaimer : i"m the author of above open source package.

PDFbox how to enable all fields in pdf form

I have a PDFForm created in Livecycle Designer. I would like import it, fill up some fields and write also like a new pdf form with enabled field. At this moment I can just fill up the fields but after export there are disabled. I also get an information: you cannot use the extended faction of pdf... How to save file with this extended pdf functions.
FileInputStream in = new FileInputStream("form.pdf");
FileOutputStream out = new FileOutputStream("test.pdf");
PDDocument document = PDDocument.loadNonSeq(in,null);
document.setAllSecurityToBeRemoved(false);
Map<String, String> values = new HashMap<String, String>();
values.put("imiePracownika", "Grzegorz K.");
setFields(document, values);
PDAcroForm form = document.getDocumentCatalog().getAcroForm();
Document documentXML = form.getXFA().getDocument();
NodeList dataElements = documentXML.getElementsByTagName("xfa:data");
if (dataElements != null) {
for (int i = 0; i < dataElements.getLength(); i++) {
setXFAFields(dataElements.item(i), values);
}
}
RandomAccessBuffer r = new RandomAccessBuffer();
COSStream cosout = new COSStream(r);
TransformerFactory.newInstance().newTransformer().transform(new DOMSource(documentXML), new StreamResult(cosout.createUnfilteredStream()));
form.setXFA(new PDXFA(cosout));
document.save(out);
document.close();
private void setFields(PDField field, Map<String, String> values) throws IOException {
List<COSObjectable> kids = field.getKids();
if (kids != null) {
for (COSObjectable pdfObj : kids) {
if (pdfObj instanceof PDField) {
setFields((PDField) pdfObj, values);
}
}
} else {
// remove the [0] from the name to match values in our map
String partialName = field.getPartialName().replaceAll("\\[\\d\\]", "");
if (!(field instanceof PDSignatureField) && values.containsKey(partialName)) {
field.setValue(values.get(partialName));
}
}
}
public void setFields(PDDocument pdfDocument, Map<String, String> values) throws IOException {
#SuppressWarnings("unchecked")
List<PDField> fields = pdfDocument.getDocumentCatalog().getAcroForm().getFields();
for (PDField pdField : fields) {
setFields(pdField, values);
}
}
public void setXFAFields(Node pNode, Map<String, String> values) throws IOException {
if (values.containsKey(pNode.getNodeName())) {
pNode.setTextContent(values.get(pNode.getNodeName()));
} else {
NodeList childNodes = pNode.getChildNodes();
if (childNodes != null) {
for (int i = 0; i < childNodes.getLength(); i++) {
setXFAFields(childNodes.item(i), values);
}
}
}
}

DataAnnotations attributes on custom control

I've used a custom control (HTML Helper) to build an Autocomplete controller.
it works great, the only thing is the validation problem.
on the client side, the validation works fine when jquery.validation.js is out of the picture, (for empty text box it gives an error message).
if the user selects something from the autocomplete, so im fine.
but when the user input is just junk, then the HttpPost needs to handle the junk & return an error message to the user.
HOW??
also, i've seen a DataAnnotation called Remote, which can manage the validation on the client side, is it better ? if so, how can i add DataAnnotaion on a custom control ??
Thank's :)
here is my code:
Index.cshtml
#using (Html.BeginForm("Index", "Create"))
{
#Html.AutocompleteFor(Url.Action("AutoCompleteServiceProviders", "Create"), true, "ex. Shower", c => c.service_id, a => a.name)
<input type="submit" id="search" value="" />
}
AutoComplete.cs
private static MvcHtmlString CreateAutocomplete<TModel>(this HtmlHelper<TModel> helper, string actionUrl, bool? isRequired, string placeholder, params Expression<Func<TModel, object>>[] expression)
{
var builder = new StringBuilder();
foreach (var item in expression)
{
var attributes = new Dictionary<string, object>
{
{ "data-autocomplete", true },
{ "data-action", actionUrl }
};
if (!string.IsNullOrWhiteSpace(placeholder))
{
attributes.Add("placeholder", placeholder);
}
if (isRequired.HasValue && isRequired.Value)
{
attributes.Add("required", "required");
}
Func<TModel, object> method = item.Compile();
var value = (Object)null;
if ((TModel)helper.ViewData.Model != null)
{
value = method((TModel)helper.ViewData.Model);
}
var baseProperty = (string)null;
var hidden = (MvcHtmlString)null;
if (item.Body is MemberExpression)
{
baseProperty = ((MemberExpression)item.Body).Member.Name;
hidden = helper.Hidden(baseProperty, value);
attributes.Add("data-value-name", baseProperty);
}
else
{
var op = ((UnaryExpression)item.Body).Operand;
baseProperty = ((MemberExpression)op).Member.Name;
hidden = helper.Hidden(baseProperty, value);
}
attributes.Add("data-value-id", "service_id");
var automcompleteName = baseProperty + "_autocomplete";
var textBox = (MvcHtmlString)null;
if (value != null)
{
textBox = helper.TextBox(automcompleteName, value, string.Empty, attributes);
}
else
{
textBox = helper.TextBox(automcompleteName, null, string.Empty, attributes);
}
builder.AppendLine(hidden.ToHtmlString());
if (baseProperty == "name")
{
builder.AppendLine(textBox.ToHtmlString());
}
}
return new MvcHtmlString(builder.ToString());
}
You can get your validation from here:
var validation = htmlHelper.ValidationMessageFor(expression, null, new Dictionary<string, object>());
UPDATE:
I use TagBuilder to create tags. What I do with tagbuilder is add that validation to a span or div tag and let the unobtrusive javascript hide/show it when needed. It returns an MVCHtmlString you can just append it to the element you want to display it in

Error while uploading file method in Client Object Model Sharepoint 2010

Error while uploading file method in Client Object Model + Sharepoint 2010. Once the file got uploaded. After that though the code compiles with no error
I get the error while executing
"{"Value does not fall within the expected range."}
{System.Collections.Generic.SynchronizedReadOnlyCollection}
I have a method which takes care of functionality to upload files
///////////////////////////////////////////////////////////////////////////////////////////
public void Upload_Click(string documentPath, byte[] documentStream)
{
String sharePointSite = "http://cvgwinbasd003:28838/sites/test04";
String documentLibraryUrl = sharePointSite +"/"+ documentPath.Replace('\\','/');
////////////////////////////////////////////////////////////////////
//Get Document List
List documentsList = clientContext.Web.Lists.GetByTitle("Doc1");
var fileCreationInformation = new FileCreationInformation();
//Assign to content byte[] i.e. documentStream
fileCreationInformation.Content = documentStream;
//Allow owerwrite of document
fileCreationInformation.Overwrite = true;
//Upload URL
fileCreationInformation.Url = documentLibraryUrl;
Microsoft.SharePoint.Client.File uploadFile = documentsList.RootFolder.Files.Add(
fileCreationInformation);
//uploadFile.ListItemAllFields.Update();
clientContext.ExecuteQuery();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
In the MVC 3.0 application in the controller I have defined the following method to invoke the upload method.
//////////////////////////////////////////////////////////////////////////////////////////////////
public ActionResult ProcessSubmit(IEnumerable<HttpPostedFileBase> attachments)
{
System.IO.Stream uploadFileStream=null;
byte[] uploadFileBytes;
int fileLength=0;
foreach (HttpPostedFileBase fileUpload in attachments)
{
uploadFileStream = fileUpload.InputStream;
fileLength=fileUpload.ContentLength;
}
uploadFileBytes= new byte[fileLength];
uploadFileStream.Read(uploadFileBytes, 0, fileLength);
using (DocManagementService.DocMgmtClient doc = new DocMgmtClient())
{
doc.Upload_Click("Doc1/Doc2/Doc2.1/", uploadFileBytes);
}
return RedirectToAction("SyncUploadResult");
}
//////////////////////////////////////////////////////////////////////////////////////////////////
Please help me to locate the error
I think your documentLibraryUrl needs to be relative. This is working for me with Sharepoint 2013
[HttpPost]
[ValidateAntiForgeryToken]
[SharePointContextFilter]
public ActionResult Upload()
{
if (Request.Files.Count > 0)
{
HttpPostedFileBase file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
using (var clientContext = spContext.CreateUserClientContextForSPHost())
{
if (clientContext != null)
{
FileCreationInformation newFile = new FileCreationInformation();
using (MemoryStream ms = new MemoryStream())
{
file.InputStream.CopyTo(ms);
byte[] array = ms.GetBuffer();
newFile.Content = array;
}
List docs = clientContext.Web.Lists.GetByTitle("Documents");
Folder folder = docs.RootFolder;
clientContext.Load(folder);
clientContext.ExecuteQuery();
newFile.Url = docs.RootFolder.ServerRelativeUrl + "/" + fileName;
Microsoft.SharePoint.Client.File uploadFile = docs.RootFolder.Files.Add(newFile);
clientContext.Load(uploadFile);
clientContext.ExecuteQuery();
//Set the metadata
Microsoft.SharePoint.Client.ListItem item = uploadFile.ListItemAllFields;
string docTitle = string.Empty;
item["Title"] = docTitle;
item.Update();
clientContext.ExecuteQuery();
}
}
}
}
return RedirectToAction("Index", new { SPHostUrl = SharePointContext.GetSPHostUrl(HttpContext.Request).AbsoluteUri });
}