I have a simple rails app that stores images created by users in an HTML5 site using canvasContext.toDataURL(). There's an index action, which shows a list of thumbnails, and a show action for each image, showing the image.
I'm being absolutely lazy in the backend here, I just store the base64 encoded data:image/png;base64,... Strings of the original image and a thumbnail, which is also generated in the client. The images are included in the page using <img src="data:"/> URIs.
Now, since this isn't fully supported in IE before v9, I will have to implement some kind of workaround, probably by including the URL of the generated image using a data- attribute and then replacing the source attribute after page load using jQuery.
This brings up the question whether it's good practice to inline rather large images into HTML using data: URLs...
Are there any recommendations or best practices? The images might be up to 500 KiB, and each of them is used only once. The page containing the images will not change once created, so it should be cacheable pretty good, including the image. The index page containing the thumbnails (which are around 60 KiB) uses pagination, so the page will hardly be cacheable. You can assume that the HTML pages will be deflated or gzipped in production.
Best-practice is: Don't.
You'll need a workaround anyway, which will completely duplicate the basic functionality, so you've just another place things can go wrong and more cases to test.
You say the images should be very cacheable, but you're sending them in a way that doesn't allow you to include information about caching. However, with the more common approach of sending images over a separate HTTP request, you can send expires and max-age requests to indicate the image won't change for a year. If you're super-confident that the image won't change you can also set a last-modified and an e-tag (you don't even need any logic to decide on the etag, just send "IMMUTABLE" as the tag) and respond to every conditional GET by sending a 304 without even checking (because again, you only do that if you're super-confident, otherwise though you can still implement a more conventional check for 304).
If the image is just used once, then have the image served by something that creates it and writes it based on some identifying features in the query (just what is specific to what you are doing) and writes it to the stream to the browser. You'll still have nicer separation within your application to have this done in its own place.
Even with gzip, you aren't going to beat this on stream size.
This is not a good idea. I would use normal, separate image resources instead.
By using inline images,
you destroy all cacheability for static images - with inlined images, every time the HTML page changes, all the image data has to be reloaded as well.
gzipping/deflating will take longer, and produce sub-optimal results (because image data is much harder and much less efficient to compress).
the rest of the Internet is geared towards HTML documents and images being separate resources: Prefetchers, caching systems, proxies...
then you have browser compatibility, which as you point out, is abysmal for older IEs.
My advice ? Don't do it.
A real file can be modified more easily with backend libraries, so don't.
And if you want use base64 in your page, you can easily do it with HTML5.
var xhr = new XMLHttpRequest();
xhr.open('GET', 'images/myimage.png', true);
xhr.responseType = 'arraybuffer';
xhr.onload = function(e){
if(this.status == 200){
var bb = new WebKitBlobBuilder(); // Or MozBlobBuilder, MSBlobBuilder, etc.
bb.append(this.response);
var reader = new FileReader();
reader.readAsDataURL(bb.getBlob('image/png'));
reader.onloadend = function(e){
$("#myimage").attr("src", reader.result);
};
}
};
xhr.send();
Related
Update:
Finally, I guess I was asking a stupid question. The jQuery creates DOM elements and it will be requested anyway. So I think it's better to use .html(xxx) to implement the feature rather than using $() to create anything before.
This is quite tricky and I never realize it before. But today I realized it's very important to a web project.
Say I have two images created dynamically:
var $img1 = $('<img>');
$img1.attr('src', 'http://domain.com/1.png');
var $img2 = $('<img>');
$img2.attr('src', 'http://domain.com/2.png');
Right after the browser runs the code above, the two images would be requested. That would be a waste of the client's and the server-side traffic.
Is it possible for me to control when the resource request be sent?
My expectation is NOT to do it by assigning src later because in my case it'd be much more complicated, the HTML code is containing a lot of stuff rather than some img tags. For example, is it possible to tell the browser that "please wait until the img tag is added onto the DOM tree"?
Append the images to DOM after the page load like this:
$(document).ready(function() {
// You could use whatever jQuery selector here you like to
// determine where to append the new elements.
// For this example, I am just appending to end of document.
$(document).append($('<img src="http://domain.com/1.png>');
$(document).append($('<img src="http://domain.com/2.png>');
});
In Objective C to build a Mac OSX (Cocoa) application, I'm using the native Webkit widget to display local files with the file:// URL, pulling from this folder:
MyApp.app/Contents/Resources/lang/en/html
This is all well and good until I start to need a German version. That means I have to copy en/html as de/html, then have someone replace the wording in the HTML (and some in the Javascript (like with modal dialogs)) with German phrasing. That's quite a lot of work!
Okay, that might seem doable until this creates a headache where I have to constantly maintain multiple versions of the html folder for each of the languages I need to support.
Then the thought came to me...
Why not just replace the phrasing with template tags like %CONTINUE%
and then, before the page is rendered, intercept it and swap it out
with strings pulled from a language plist file?
Through some API with this widget, is it possible to intercept HTML before it is rendered and replace text?
If it is possible, would it be noticeably slow such that it wouldn't be worth it?
Or, do you recommend I do a strategy where I build a generator that I keep on my workstation which builds each of the HTML folders for me from a main template, and then I deploy those already completed with my setup application once I determine the user's language from the setup application?
Through a lot of experimentation, I found an ugly way to do templating. Like I said, it's not desirable and has some side effects:
You'll see a flash on the first window load. On first load of the application window that has the WebKit widget, you'll want to hide the window until the second time the page content is displayed. I guess you'll have to use a property for that.
When you navigate, each page loads twice. It's almost not noticeable, but not good enough for good development.
I found an odd quirk with Bootstrap CSS where it made my table grid rows very large and didn't apply CSS properly for some strange reason. I might be able to tweak the CSS to fix that.
Unfortunately, I found no other event I could intercept on this except didFinishLoadForFrame. However, by then, the page has already downloaded and rendered at least once for a microsecond. It would be great to intercept some event before then, where I have the full HTML, and do the swap there before display. I didn't find such an event. However, if someone finds such an event -- that would probably make this a great templating solution.
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
DOMHTMLElement * htmlNode =
(DOMHTMLElement *) [[[frame DOMDocument] getElementsByTagName: #"html"] item: 0];
NSString *s = [htmlNode outerHTML];
if ([s containsString:#"<!-- processed -->"]) {
return;
}
NSURL *oBaseURL = [[[frame dataSource] request] URL];
s = [s stringByReplacingOccurrencesOfString:#"%EXAMPLE%" withString:#"ZZZ"];
s = [s stringByReplacingOccurrencesOfString:#"</head>" withString:#"<!-- processed -->\n</head>"];
[frame loadHTMLString:s baseURL:oBaseURL];
}
The above will look at HTML that contains %EXAMPLE% and replace it with ZZZ.
In the end, I realized that this is inefficient because of page flash, and, on long bits of text that need a lot of replacing, may have some quite noticeable delay. The better way is to create a compile time generator. This would be to make one HTML folder with %PARAMETERIZED_TAGS% inside instead of English text. Then, create a "Run Script" in your "Build Phase" that runs some program/script you create in whatever language you want that generates each HTML folder from all the available lang-XX.plist files you have in a directory, where XX is a language code like 'en', 'de', etc. It reads the HTML file, finds the parameterized tag match in the lang-XX.plist file, and replaces that text with the text for that language. That way, after compilation, you have several HTML folders for each language, already using your translated strings. This is efficient because then it allows you to have one single HTML folder where you handle your code, and don't have to do the extremely tedious process of creating each HTML folder in each language, nor have to maintain that mess. The compile time generator would do that for you. However -- you'll have to build that compile time generator.
To offer a mobile version of an existing mediawiki installation I was looking for a practicable way to remove all images from output. The most preferred solution would be one where the generated html would no longer contain the image-tags.
As I was not able to figure out a clean solution I moved the images to a different server and disabled $wgForeignFileRepos and $wgAllowExternalImages in this version.
Unfortunately - while the images are not shown - there appears a placeholder box containing the image's name and a (now not functioning) link to it.
Do you know about a way to get rid of the images without using css/js or a way to bring my approach to completion?
You could use this javascript mobile browswer detection and then on detection it runs the following javascript code.
var imagesremove = document.getElementsByTag('img')
imagesremove.parentNode.removeChild(imagesremove);
There are probably better solutions, but you can override the ImageBeforeProduceHTML hook to make images generate empty output:
$wgHooks['ImageBeforeProduceHTML'][] = function( &$skin, &$title, &$file, &$frameParams, &$handlerParams, &$time, &$res ) {
$res = '';
return false;
}
...or something more fancy, such as returning a link to the image instead of an actual <img> tag.
Depending on your wiki's caching settings, you might have to purge the page cache afterwards, e.g. by setting $wgCacheEpoch.
So I'm working on a web app, and I want to filter search results.
A nice restful implementation might look like this:
1. mysite.com/clothes/men/hats+scarfs
But lets say we want to ajax up the filtering, like the cool kids, and we want to retain deep linking, we might use the anchor tag and parse that with Javascript to show the correct listings:
2. mysite.com/clothes#/men/hats+scarfs
However, if someone clicks the first link with JS enabled, and then changes filters, we might get:
3. mysite.com/clothes/men/hats+scarfs#/women/shoes
Urk.
Similarly, if someone does not have JS enabled, and clicks link 2 - JS will not parse the options and the correct listings will not be shown.
Are Ajax deep links and non-Ajax links incompatible? It would seem so, as servers cannot parse the # part of a url, since it is not sent to the server.
There's a monkeywrench being thrown into this issue by Google: A proposal for making Ajax crawlable. Google is including recommendations for url structure there that may give you ideas for your own application.
Here's the wrapup:
In summary, starting with a stateful
URL such as
http://example.com/dictionary.html#AJAX
, it could be available to both
crawlers and users as
http://example.com/dictionary.html#!AJAX
which could be crawled as
http://example.com/dictionary.html?_escaped_fragment_=AJAX
which in turn would be shown to users
and accessed as
http://example.com/dictionary.html#!AJAX
View Google's Presentation here (note: google docs presentation)
In general I think it's useful to simply turn off JavaScript and CSS entirely and browse your website and web application and see what ends up getting exposed. Once you get a sense of what's visible, you will understand what most search engines see and that in turn will show you what is and is not getting spidered.
If you go to mysite.com/clothes/men/hats+scarfs with JavaScript enabled then your JavaScript should automatically rewrite that to mysite.com/clothes#men/hats+scarfs - when you click on a filter, they should be controlled by JavaScript meaning you'll only change the hashtag rather than the entire URL (as you're going to have return false anyway).
The problem you have is for non-JS users going to your JS enabled deeplinks as the server can't determine that stuff. Unfortunately, the only thing you can do is take them to mysite.com/clothes and make them start their journey again (as far as I'm aware). You'll need to try and ensure that when people link to the site, they use the hardcoded deeplink rather than the hashed deeplink
I don't recommend ever using the query string as you are sending data back to the server without direct relevance to the prior specified destination. That is a corruptible security hole as malicious code can be manually added to the query string to cause a XSS or buffer overflow attack at your webserver.
I believe REST was intended to work with absolute URIs without a query string, because then your specifying only a location of a resource and it is that location that is descriptive and semantically relevant in addition to the possibility of the resource being so equally relevant. Even if there is no resource at the specified path you have still instantiated a potentially unique and descriptive location that can be processed accordingly.
Users entering the site via deep links
Nonsensical links (like /clothes/men/hats#women/shoes) can be avoided if you construct your Ajax initialisation code in such a way that users who enter the site on filtered pages (e.g. /clothes/women/shoes) are taken to the /clothes page before any Ajax filtering happens. For example, you might do something like this (using jQuery):
$("a.filter")
.each(function() {
var href = $(this).attr("href").replace("/clothes/", "/clothes#");
$(this).attr("href", href);
})
.click(function() {
update_filter($(this).attr("href").split("#")[1]);
});
Users without JavaScript
As you said in the question, there's no way for the server to know about the URL fragment so filtering would not be applied for users without JavaScript enabled if they were given a link to /clothes#filter.
However, even without filtering, these links could be made more meaningful for non-JS users by using the filter strings as IDs in your /clothes page. To prevent this messing with the Ajax experience the IDs would need to be changed (or the elements removed) with JavaScript before the Ajax links were initialised.
How practical this is depends on how many categories you have and what your /clothes page contains.
Is it possible to create data URI's in GWT?
I want to inject a byte array image as an actual image using a data URI.
You should checkout ClientBundle in GWT's trunk. It will create data urls automatically for browsers that support them and fallbacks for that other browser: http://code.google.com/p/google-web-toolkit/wiki/ClientBundle
The feature won't ship until GWT 2.0, but it's in heavy use now.
Yes. It is completely possible to do this. I'd done it for an application until I realized IE6 doesn't handle binary data streams this way. You can do it in several ways. For the purposes of my example, I'm already assuming that you've converted the byte array to a string somewhere, and that it is properly encoded and of the proper type for your data URI. I'm also assuming you know the basic format (or can find it) of your chosen data scheme.
I've taken these examples from the Wikipedia article on data URI scheme.
The first is to just use raw HTML to make the image reference as you normally would and have it inserted into the page.
HTML html = new HTML("<img src=\"data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP
C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA
AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J
REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq
ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0
vr4MkhoXe0rZigAAAABJRU5ErkJggg==\" alt=\"Red dot\">");
You can also just use an image. (Which should produce roughly the same output HTML/JS.)
Image image = new Image("data:image/png;base64,
iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGP
C/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IA
AAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1J
REFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jq
ch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0
vr4MkhoXe0rZigAAAABJRU5ErkJggg==");
This allows you to use the full power of the Image abstraction on top of your loaded image.
I'm still thinking that you may want to expand on this solution and use GWT's deferred binding mechanism to deal with browsers that do not support data URIs. (IE6,IE7)