THE QUESTION Is there some .net, ASP, MVC or even IIS optimizations I'm not taking in account? or is this whole approach simply a bad idea?
SCENARIO
The following works 0K
I have an ImageController that allows me to optimize and cache images on the fly.
The actual call looks like this: /image/user/1?width=200&height=200
And the controller itself like this:
[AllowAnonymous] // TODO author/revise
public class ImageController : FileController
{
private FileResult Image(string entity, long id, int? width, int? height, Position? position=null)
{
string file = base.ImageHandler.ImagesPaths(Paths.img, entity, id.ToString()).FirstOrDefault();
if (string.IsNullOrEmpty(file))
{
throw new HttpException(404, "File not found");
}
// implicit else
string extension = Path.GetExtension(file);
System.Drawing.Imaging.ImageFormat oImageFormat = WebFileSystemImageHandler.ImageFormat(extension);
if ((null != width) || (null != height))
{
// vvv Beginning of IS THERE SOMETHING WRONG WITH THIS? vvv
string path = string.Format("{0}{1}/{2}/", Paths.img, entity, id);
string pathServer = Server.MapPath(path);
if (!Directory.Exists(pathServer))
Directory.CreateDirectory(pathServer);
string pattern = "";
if (null != width ) pattern += width.ToString(); // 500
if (null != height) pattern += "x" + height.ToString(); // 500x400, or x400 w/o width
string cache = Directory.GetFiles(pathServer, pattern).FirstOrDefault();
// ^^^ End of IS THERE SOMETHING WRONG WITH THE ABOVE ^^^
if (string.IsNullOrEmpty(cache))
{// no cache? sure let's get you started!
Image oImage = System.Drawing.Image.FromStream( new MemoryStream( System.IO.File.ReadAllBytes(file) ) );
file = Server.MapPath(path + pattern + extension);
Bitmap oBitmap = new Bitmap(oImage);
oBitmap = oBitmap.Adjust(width:width, height:height, position:position);
oBitmap = oBitmap.Compress();
oBitmap.Save(file, oImageFormat);
}
else
file = cache;
}
MemoryStream oStream = new MemoryStream( System.IO.File.ReadAllBytes(file) );
string mime = string.Format("image/{0}", extension.Replace(".", ""));
return new FileContentResult(oStream.ToArray(), mime);
}
Where base.ImageHandler.ImagesPaths basically returns a Directory.GetFiles("{folder}\{entity}\", "{id}.*").FirstOrDefault()
THE PROBLEM When I have a gallery page with multiple of these images, the html renders immediately but the images show empty and start popping 1 by 1 (I imagine this is due to 1 call to the controller per session). I would expect this as expected behavior the 1st run only, but happens every time
As a side-comment:
If your application is using HttpSession, make sure it is either disabled for this Controller or at least read-only, using:
[SessionState(SessionStateBehavior.ReadOnly)]
That way at least 2 requests can execute in parallel.
Also, if the amount of images and sizes will be limited and not too large, you might be able to simplify all this by just using OutputCache both at the client and the server setting (in memory). You'd avoid writing the images to physical storage, and resized output would get reused between users.
I wouldn't advise you use GDI for image resizing at all in MVC, as it's not supported and can cause memory leaks and other issues if not used carefully (see http://weblogs.asp.net/bleroy/archive/2009/12/10/resizing-images-from-the-server-using-wpf-wic-instead-of-gdi.aspx).
You should look at alternatives such as using Windows Imaging Components, or for simplicity and additional features use Imageresizer (http://imageresizing.net/)
Also see bertrand's follow up posts: http://weblogs.asp.net/bleroy/archive/2010/05/03/the-fastest-way-to-resize-images-from-asp-net-and-it-s-more-supported-ish.aspx
and http://weblogs.asp.net/bleroy/archive/2011/10/22/state-of-net-image-resizing-how-does-imageresizer-do.aspx for advice on the various approaches to achieving fast web-based resizing
Related
How to upload multiple images?
I want to upload multiple images into the database; I select the multiple images and then hit the upload button, but it inserts only one image. Can any expert tell me where I am going wrong?
Image class:
public partial class Image
{
public int imgID { get; set; }
public string ImgPath { get; set; }
public long ProdID { get; set; }
public virtual Product Product { get; set; }
}
Controller:
public ActionResult AddNewProducts(ProductViewModel prod, List file)
{
try
{
List PTlist = _IproductType.PTList();
ViewBag.Ptlist = new SelectList(PTlist, "PType_ID", "P_Name");
// Product Color List
List pColorList = _IProductColor.PColorlist();
ViewBag.pColor_List = new SelectList(pColorList, "C_ID", "C_Name_OR_Code");
List pSizeList = _ISize.pSizeList();
ViewBag.pSizeLists = new SelectList(pSizeList, "S_ID", "S_Size");
string PathDB = string.Empty;
Image img = new Image();
foreach (HttpPostedFileBase files in file)
{
string filename = Path.GetFileName(files.FileName);
string _filename = DateTime.Now.ToString("yymmssff") + filename;
string extension = Path.GetExtension(files.FileName);
string path = Path.Combine(Server.MapPath("~/Upload/"), _filename);
PathDB = "~/Upload/" + _filename;
if (extension.ToLower() == ".jpeg" || extension.ToLower() == ".jpg" || extension.ToLower() == ".png")
{
if (files.ContentLength <= 1000000)
{
img = new Image();
img.imgID = prod.ImgID;
img.ImgPath = PathDB;
img.ProdID = prod.ProductID;
}
else
ViewBag.sizemsg = "Size limit exceeded";
}
else
ViewBag.fileformat = "File is not the correct format";
}
}
}
As you referenced the MCV pattern and used terms like "upload", I am going to asume this is Web Development. I am also asuming your pattern is:
user uploads a bunch of images
you do some processing on the images
you present the processed images, so the user can select one
that one is actually persisted
The "reconsider this path" answer
If you do, that is definitely a bad approach. Transfering bulk data to only actually use a fraction of it? A very common beginners mistake with Web and Database Development. Putting a lot of CPU work on the Server side is another one.
Whatever processing you are doing, you should ideally be doing it on the client side: Browser Plugin. Custom Client Side applicaiton. JavaScript element. Even just demanding a specific format from the user. That sort of thing. And then only upload that specific image.
If the server never has to see a bit from those images you do not want, it is a good design.
Staging table/database
This is the asnwer you are really comited to your approach or it is not a option for some reason. In order to have the server process and display a image, it must be saved on the server side.
In memory is not really an option. With a massively paralell design and ThreadPools of 50-100 Threads per CPU core, WebServers are notoriously easy to run out of resources, particular memory. And that asumes you can actually persist something in memory like that to begin with (the design does not make that easy, to get you off that path).
So the next best bet would be a staging table or temp directory. Something where you can:
store the images you just uploaded
have some background process process them
you can hand the processed images out of
you can transfer the one image the user wants into the proper persistent database
something that is regualry clearing up any data the user never made a decision on
Personally I would advise for a seperate set of DB Tables. In our day, temp direcotiries are a security vulnerability that no admin should accept without looking for alternatives.
Currently, I use this code to extract text from a Rectangle (area).
public static class ReaderExtensions
{
public static string ExtractText(this PdfPage page, Rectangle rect)
{
var filter = new IEventFilter[1];
filter[0] = new TextRegionEventFilter(rect);
var filteredTextEventListener = new FilteredTextEventListener(new LocationTextExtractionStrategy(), filter);
var str = PdfTextExtractor.GetTextFromPage(page, filteredTextEventListener);
return str;
}
}
It works, but I don't know if it's the best way to do it.
Also, I wonder if the GetTextFromPage could be improved by the iText team to increase its performance, since I'm processing hundreds of pages in big PDFs and it usually takes more than 10 minutes to do it using my current configuration.
EDIT:
From the comments: It seems that iText can extract the text of multiple rectangles on the same page in one pass, something that can improve the performance (batched operations tend to be more efficient), but how?
MORE DETAILS!
My goal is to extract data from a PDF with multiple pages. Each page has the same layout: a table with rows and columns.
Currently, I'm using the method above to extract the text of each rectangle. But, as you see, the extraction isn't batched. It's only a rectangle at a time. How could I extract all the rectangles of a page in a single pass?
As already mentioned in a comment, I was surprised to see that the iText 7 LocationTextExtractionStrategy does not anymore contain something akin to the iText 5 LocationTextExtractionStrategy method GetResultantText(TextChunkFilter). This would have allowed you to parse the page once and extract text from text pieces in arbitrary page areas out of the box.
But it is possible to bring back that feature. One option for this would be to add it to a copy of the LocationTextExtractionStrategy. This would be kind of a long answer here, though. So I used another option: I use the existing LocationTextExtractionStrategy, and merely for the GetResultantText call I manipulate the underlying list of text chunks of the strategy. Instead of a generic TextChunkFilter interface I restricted filtering to the criteria at hand, the filtering by rectangular area.
public static class ReaderExtensions
{
public static string[] ExtractText(this PdfPage page, params Rectangle[] rects)
{
var textEventListener = new LocationTextExtractionStrategy();
PdfTextExtractor.GetTextFromPage(page, textEventListener);
string[] result = new string[rects.Length];
for (int i = 0; i < result.Length; i++)
{
result[i] = textEventListener.GetResultantText(rects[i]);
}
return result;
}
public static String GetResultantText(this LocationTextExtractionStrategy strategy, Rectangle rect)
{
IList<TextChunk> locationalResult = (IList<TextChunk>)locationalResultField.GetValue(strategy);
List<TextChunk> nonMatching = new List<TextChunk>();
foreach (TextChunk chunk in locationalResult)
{
ITextChunkLocation location = chunk.GetLocation();
Vector start = location.GetStartLocation();
Vector end = location.GetEndLocation();
if (!rect.IntersectsLine(start.Get(Vector.I1), start.Get(Vector.I2), end.Get(Vector.I1), end.Get(Vector.I2)))
{
nonMatching.Add(chunk);
}
}
nonMatching.ForEach(c => locationalResult.Remove(c));
try
{
return strategy.GetResultantText();
}
finally
{
nonMatching.ForEach(c => locationalResult.Add(c));
}
}
static FieldInfo locationalResultField = typeof(LocationTextExtractionStrategy).GetField("locationalResult", BindingFlags.NonPublic | BindingFlags.Instance);
}
The central extension is the LocationTextExtractionStrategy extension which takes a LocationTextExtractionStrategy which already contains the information from a page, restricts these information to those in a given rectangle, extracts the text, and returns the information to the previous state. This requires some reflection; I hope that is ok for you.
In addin for Sparx EA I use this code to get pictures and assign to entity. Then I use images from entities, to, as example, save at some folders or insert in word report etc (from this answer)
/// <summary>
/// Access to diagram image without using clipboard
/// </summary>
/// <param name="projectInterface">Ea Sparx interface</param>
/// <param name="eaDiagramGuid">Guid of the diagramm</param>
/// <returns></returns>
public static Image GetDiagramImage(this Project projectInterface, Guid eaDiagramGuid, ApplicationLogger _logger)
{
Image diagramImage;
try
{
var diagramByGuid = projectInterface.GUIDtoXML(eaDiagramGuid.ToString("B"));
string tempFilename = string.Format("{0}{1}{2}", Path.GetTempPath(), Guid.NewGuid().ToString(), ".png");
bool imageToFileSuccess = projectInterface.PutDiagramImageToFile(diagramByGuid, tempFilename, FileExtensionByName);
if (imageToFileSuccess)
{
using (var imageStream = new MemoryStream(File.ReadAllBytes(tempFilename)))
{
diagramImage = Image.FromStream(imageStream);
}
File.Delete(tempFilename);
}
else
{
throw new Exception(string.Format("Image to file exprot fail {0}", projectInterface.GetLastError()));
}
}
catch (Exception e)
{
throw;
}
return diagramImage;
}
The problem is - it works if project I work with saved as .eap file.
If it's .feap file, which, as I believe means that it works with Firebird database (instead of Access), all saved/exproted to report images are blank, like this down below
Why does it happens and is there workaround?
UPD
It works if I use projectInterface.PutDiagramImageOnClipboard instead but I don't wont to use clipboard at all
UPD 2
After some experiments today at the morning (at my timezone, gmt+3, it's still morning) I found out that the problem was with GUIDs register!
After I decided to apply .toUpper() here
var diagramByGuid = projectInterface.GUIDtoXML(eaDiagramGuid.ToString("B").ToUpper());
it started work fine!
Strange thing thou that if project is *.EAP type everything works even when guid is not in upper register!
UPD3
Well, unfortunately, I was wrong. Some pictures are still blank. But somehow that changes got impact on diagrams, I keep testing this stuff.
And some of the pictures are appeared twice or in wrong place.
But it's kinda interesting (if I could say so) behaviour.
UPD 4
I was wrong in my UPD2 part! GUID can contain down register symbols as well as upper ones.
So, first I removed that part.
What I done next - I started to pass GUID directly from diagram, so signature changed like that
public static Image GetDiagramImage(this Project projectInterface, string eaDiagramGuid, ApplicationLogger _logger)
and eaDiagramGuid should be passed right from EA.Diagram object.
When we parse it as Guid by Guid.Parse(eaDiagramGuid) it convert everything in lowercase like here
so, thats why I got the problem.
But for some reason it was not appeared in *.EAP type of projects!
Also it strange that register matters in that case, really. Seems like GUID in common and GUID in sparx ea are different things!
Okay, as I founded out here, the thing is, in case of *.FEAP all items GUIDs are surprisingly case sensetive.
So, my mistake was to store item GUID as Guid type - when we use Guid.Parse(myGuidString) function and then we converting it back to string - all symbols are appers to be in lowercase.
Also, I got my answer from support (they were surprisingly fast, I really like that)
Hello Danil,
Running over the Stack Overflow - it sounds like it's effectively
answered. The core point is that the Feap files are case sensitive.
To be technical, the collation used for our Firebird databases is case
sensitive.
So, in the Automation script you do need to adhere to the case.
So, I just change things to work directly with GUID string from project item like
Image diagramImage = _eaProjectInterface.GetDiagramImage(diagram.DiagramGUID, _logger);
and function now look like this
public static Image GetDiagramImage(this Project projectInterface, string eaDiagramGuid, ApplicationLogger _logger)
{
Image diagramImage;
try
{
var diagramByGuid = projectInterface.GUIDtoXML(eaDiagramGuid);
string tempFilename = string.Format("{0}{1}{2}", Path.GetTempPath(), Guid.NewGuid().ToString(), ".png");
bool imageToFileSuccess = projectInterface.PutDiagramImageToFile(diagramByGuid, tempFilename, FileExtensionByName);
if (imageToFileSuccess)
{
using (var imageStream = new MemoryStream(File.ReadAllBytes(tempFilename)))
{
diagramImage = Image.FromStream(imageStream);
}
File.Delete(tempFilename);
}
else
{
throw new Exception(string.Format("Image to file exprot fail {0}", projectInterface.GetLastError()));
}
}
catch (Exception e)
{
throw;
}
return diagramImage;
}
So, this means that Sparx EA *.FEAP project GUIDs are NOT really GUIDs but just string-keys (as I assume).
Be careful when your work with them :-)
Hello All I have various web cameras i would like to embed in my site
http://81.137.212.183:4483/GetData.cgi
The problem is at times the cameras go down so i need to check they are active in c# before attempting to render:
<img height="240" width="320" src="http://81.137.212.183:4483/GetData.cgi" />
Please can someone advise how i can check the cgi is active in c# or any other recommendation. If i simple load the cgi and it is down it causes the browser to crash
One recommendation was to use the code below:
The problem with the below approach is the site is forever loading and a fav icon is never shown as can be seen http://www.camsecure.co.uk/
newImage = new Image();
function LoadNewImage() {
var unique = new Date();
document.images.webcam.src = newImage.src;
newImage.src = "http://collectart.dyndns.org:4484/Jpeg/CamImg.jpg?time=" + unique.getTime();
}
function InitialImage() {
var unique = new Date();
newImage.onload = LoadNewImage();
newImage.src = "http://collectart.dyndns.org:4484/Jpeg/CamImg.jpg?time=" + unique.getTime();
document.images.webcam.src = "http://collectart.dyndns.org:4484/Jpeg/CamImg.jpg?time=" + unique.getTime();
document.images.webcam.onload = "";
}
First off, you need to put some security over that first link. It appears the camera settings are public and available to anyone.
If the only problem is the long loading times slowing the rest of the site down, you could load the images in an iframe rather than directly in an image tag -- then the hang is only in the iframe:
<iframe src="http://81.137.212.183:4483/Simple/home.htm?IMG"></iframe>
To check the IP camera is up, you could simply try to get it's host page:
using System.Net.Http;
...
var uri = new Uri("http://81.137.212.183:4483/Simple/index.htm");
var task = new HttpClient().GetAsync(uri);
if (task.Wait(TimeSpan.FromSeconds(1)) && task.Result.IsSuccessStatusCode)
{
// SUCCESS!
}
else
{
// FAILURE... try next camera
}
However, it looks like the image .cgi location can still fail if the camera is available. In that case it would be best to load in an iframe even if you get success.
In Revit 2013 I have tool that I'm making that copies dimensions from one drafting view to another. I've got it to properly create a new version of a dimension including the Curve, DimensionType, and References but I'm having trouble with the properties Above, Below, Prefix, and Suffix. They copy just fine if at least one of them has a value. However, if none of them have a value then it will throw an AccessViolationException when I try to access them. I have tried to catch that exception but it bubbles up and it crashes Revit (I'm assuming it's caused by native code that fails).
How can I check to see if these properties have any value when I do my copying without triggering this AccessViolationException?
Autodesk Discussion Group Question
The DimensionData class is my own used for storing the dimension information so that it can be used to create the dimension in a separate document.
private IEnumerable<DimensionData> GetDimensionDataSet(Document document,
View view)
{
if (document == null)
throw new ArgumentNullException("document");
if (view == null)
throw new ArgumentNullException("view");
List<DimensionData> dimensionDataSet = new List<DimensionData>();
FilteredElementCollector dimensionCollector =
new FilteredElementCollector(document, view.Id);
dimensionCollector.OfClass(typeof(Dimension));
foreach (Dimension oldDimension in dimensionCollector)
{
Line oldDimensionLine = (Line)oldDimension.Curve;
string dimensionTypeName = oldDimension.DimensionType.Name;
List<ElementId> oldReferences = new List<ElementId>();
foreach (Reference oldReference in oldDimension.References)
oldReferences.Add(oldReference.ElementId);
DimensionData dimensionData;
try
{
string prefix = oldDimension.Prefix;
dimensionData = new DimensionData(oldDimensionLine,
oldReferences,
dimensionTypeName,
prefix,
oldDimension.Suffix,
oldDimension.Above,
oldDimension.Below);
}
catch (AccessViolationException)
{
dimensionData = new DimensionData(oldDimensionLine,
oldReferences, dimensionTypeName);
}
dimensionDataSet.Add(dimensionData);
}
return dimensionDataSet;
}
Regarding transactions: As far as I'm aware, you are only required to be inside a transaction when you are making any sort of CHANGE (modifications, deletions, additions). If all you are doing is collecting dimension information, you would not need a transaction, but when you use that information to create new dimensions in another document, that code would have to be inside a transaction. I have had a number of programs under development which did not yet modify the document but simply collected parameter settings and posted them to a TaskDialog.Show(). These programs worked fine, and I don't see anything in your code that actually modifies your model, so that doesn't seem to be your issue.
It seems like I bug.
Can you post the issue to the ADN Support?
The solution I can suggest is to use Parameters of the Dimension element instead of Dimension class properties.
For example, you can get Suffix and Prefix by following code
var suffixParameter =
oldDimension.get_Parameter(BuiltInParameter.SPOT_SLOPE_SUFFIX);
string suffix = null;
if (suffixParameter != null)
{
suffix = suffixParameter.AsString();
}
var prefixParameter =
oldDimension.get_Parameter(BuiltInParameter.SPOT_SLOPE_PREFIX);
string prefix = null;
if (prefixParameter != null)
{
prefix = prefixParameter.AsString();
}
Unfortunatelly, I don't tell you how to get Above and Below Properties via parameters, because I don't have a project to test. But you can easily determine parameters using BuiltInParameter Checker
Hope it helps.