In my webapp the user has the option to download a file containing some data, which they do by clicking on a button. For small amounts of data the file starts downloading pretty much immediately and that shows in the browser's download area. Which is good.
For large amounts of data it can take the server a substantial amount of time to calculate the data, even before we start downloading. This is not good. I want to indicate that the calculation is in progress. However I don't want to put a "busy" indicator on my UI, because the action does not block the UI - the user should be able to do other things while the file is being prepared.
A good solution from my point of view would be to start the download process before I have finished the calculation. We always know (or can quickly calculate) the first few hundred bytes of the file. Is there a mechanism where I can have the server respond to a download request with those few bytes, thus starting the download and making the file show up in the download area, and provide the rest of the file when I have finished calculating it? I'm aware that it will look like the download is stalled, and that's not a problem.
I can make a pretty good estimate of the file size very quickly. I would prefer not to have to use a third-party package to achieve this, unless it's a very simple one. We are using Angular but happy to code raw JS if needed.
To indicate that the link points to a download on the client, the easiest way is the download attribute on the link. The presence of the attribute tells the browser not to unload the current tab or create a new one; the value of the attribute is the suggested filename.
For the back-end part, after setting the correct response headers, just write the data to the output stream as it becomes available.
You asked for a general solution
1) First, at your HTML/JS you can prevent the UI from being blocked by setting you download target to any other WebPage, the preferred way for doing this is to set the target to an IFRAME:
<!-- your link must target the iframe "downloader-iframe" -->
<a src="../your-file-generator-api/some-args?a=more-args" target="downloader-iframe">Download</a>
<!-- you don't need the file to be shown -->
<iframe id="downloader-iframe" style="display: none"></iframe>
2) Second, at your back-end you'll have to use both Content-Disposition and Content-Length(optional) headers, be careful using the "length" one, if you miss calculate the fileSize it will not be downloaded. If you don't use Content-Length you'll not see the "downloading progress".
3) Third, at you'r back-end you have to make sure that you are writing your bytes directly at your response! that way your Browser and your Web-Server will know that the download is "in progress",
Example for Java:
Using ServletOutputStream to write very large files in a Java servlet without memory issues
Example for C#:
Writing MemoryStream to Response Object
HOW this 3 steps are built will be up to you, frameworks and libraries you are using, for example Dojo & JQuery have great IFRAME manipulation utilities, all thought you can do the coding by yourself, this is a JQuery sample:
Using jQuery and iFrame to Download a File
Also:
Adding a "busy" animation is ok! you just have to make sure that it's not blocking you'r UI, something like this:
I am attempting to create a Click Here link in my report and have it open an image (file) in a new tab instead of replacing the report page in the browser.
=Fields!ManufacturerDataPlatePhoto.Value
(Returns
file://cld-sql-01/1234/Web_live_FileUploads/iOS%20Applications/MyAssets/images/Live/20%20May%202019/asset70-13-RES8743.jpg)
This returns the image correctly, but on the same page as the report so the user would have to click back in her browser and rerun the report.
="javascript:void(window.open('file:///" & Fields!ManufacturerDataPlatePhoto.Value & "','_blank'))"
(Returns
file://cld-sql-01%5C1234%5CSEG_Web_live_FileUploads%5CiOS%20Applications%5CMyAssets%5Cimages%5CLive%5C20%20May%202019%5Casset70-13-RES8743.jpg)
Opens a new tab, but with a broken link, because the forward slashes have been replaced with %5C.
Any ideas how I could put the forward slashes back in?
You need to do URL Encoding for your url, there's a good article that discuss the details on how you can do it in SSRS, here's the link
Ultimately any solution with JavaScript or VB Functions became too difficult because of all the ways different browsers and their versions display (or completely block) network file paths access.
It was necessary in the end to simply add a thumbnail image in the report using the image path I pulled from the DB and a note below the image to "Right-Click then Save Image".
This also provided an elegant way to provide the image automatically in Excel, Word, etc, and providing the path in the ToolTip added additional functionality to the report the client hadn't thought of as well.
Thank you Tholitz_Reloaded for your answer, I'm sorry I cannot upvote you now, but I do not have >15 reputation yet on this account yet.
Final solution results
In my web app, I have a large collection of thumbnails, the user is able to select a thumbnail and client side recrop from the original image to recreate a new thumbnail.
That's fine, in my app, I just set the newly created image instantly to the image source, without reloading it from the server, and next to this, the new image is uploaded to the server. This is to ensure a very responsive feeling. The problem is that when to user refreshes the page, he sees the cached old version of the thumbnail.
I know I could use some image.jpg?sometimestamp to be sure the browser has to download a new version of the thumbnail, but as I said, the app needs to be very responsive, even on small &slow internet connections. (Thats why the app in itself is stored on the user's computer and not downloaded live. Only uploads, downloads and jsons are transitting)
The ideal solution would be to be able to tell the browser : remove this particular file from your cache : someurl.com/somefolder/image.jpg, so that the browser has to fetch it again when it needs it. is this possible and how?
So I'm not asking how not to cache files or how to force re-verification each call, I'm asking how I could remove some particular file from browser's cache.
ps: this can be a webkit-only solution as this is the only platform it is running on.(it is actually a qtwebkit on a Qt project.
The function window.URL.revokeObjectURL() can solve your problem.
The URL.revokeObjectURL() static method releases an existing object
URL which was previously created by calling
window.URL.createObjectURL(). Call this method when you've finished
using a object URL, in order to let the browser know it doesn't need
to keep the reference to the file any longer.
For details: https://developer.mozilla.org/en-US/docs/Web/API/URL.revokeObjectURL
Note that this is an experimental function with limited cross-browser support.
You can't. Append a ?[something_volatile] to the request when you need to download it again, and you'll get the same effect. That method won't take away any of the snappiness if you only change the appendage string when needed.
Sorry for necropost, but I think it could be helpful for others in the same situation:
When your upload is done, perform an AJAX get request with no-cache header.
By specifing this in the REQUEST, you tell the browser to ignore cache and to perform a new request to server.
Assuming I have a cached image at /images/jondoe.jpg, it could be like this :
Code (using Axios):
Axios.post('/upload/new-image', FormDataObject).then(() => {
Axios.get('/images/jondoe.jpg', {headers: {'Cache-Control': 'no-cache'}}).then(reloadPageFunction)
// Now you'll have a new image in your cache
})
Maybe this link is useful for u, according to it, u can use below code :
caches.open('v1').then(function(cache) {
cache.delete('/images/image.png').then(function(response) {
someUIUpdateFunction();
});
})
I figure I'd demonstrate the problem with an example first,
jsfiddle: http://jsfiddle.net/e2UfM/15/
(Tested with FF 12, and Chrome 18.0.1025.168)
Usage:
Load in a text file from your local machine.
Hit "load file".
Hit "display file size" - note the size.
modify & save the text file on your local machine.
Hit "display file size" again. Note how in webkit browsers (Chrome) the file size changes, but in Firefox it didn't update the file size
Non-webkit browsers do not update their size attribute when users make changes to the local file that they have selected whereas Chrome for example does. Both browsers update the contents of the file.
Is there a way to get Firefox to update the file size similar to how Chrome does in this situation?
Simple Real World Example:
User selects a file that's too large for the form, they hit the submit button and get notified that their file is too large (via an alert, "size" bar (see below), etc)
They modify the file locally, and hit submit again.
In Chrome, the file size updates. When the user hits the submit button again, it will validate it's updated size once more and allow the upload. In Firefox, the user must re-select the file on the form before it will see the file size change.
A partial workaround for Firefox - #ZER0's answer
Real world example (in-depth):
One purpose of the File API is to verify file sizes on the client side before uploading to a server if I'm not mistaken.
Consider the scenario where there is a 2MB upload limit in a form and the user chooses a 1MB file. Both Firefox and Chrome will see that the file size is less than 2MB and add it to the form. Let's also say there is a neat bar that shows how big of a file they have chosen, and if it meats the file size limit or not:
But then the user decides to make a minor change to the contents of that file locally before they submit the form and bump the size over 2MB.
In Google Chrome, I can handle this gracefully on the client side. I can check the file size again when the user submits the form, and verify that it is still in fact under 1MB before sending it to the server. But even before the user submits the form, in Chrome, I can go as far as updating the little bar image dynamically as they make changes locally as such:
This "bar" (or any other form on instant notification such as an alert) is useful if the user is filling out a large form. I'd like the user to know instantly when their file is too large and so that they can correct it then, and not just when they submit the form.
In Firefox, because the file size never updates, it will gladly upload the 2MB file thinking that it is still 1MB! I use server side logic to double check the file size, but I'd rather save a server trip.
How I came across the bug:
The above examples are in place to relate to more people as more people have probably dealt with file uploads in forms vs. using the slice function in the File API. This is specifically how I am running into the issue.
In my form, the user selects a file and when they hit submit only the last 10,000 bytes are displayed on the screen in a textarea to confirm that it's really the file that they want.
Consider a file with a size of 50,000 bytes. A user chooses it in the form, and both Chrome and Firefox show bytes 40,000 - 50,000 in the textarea.
Now the user adds some content to the file, and bumps the same file to 70,000 bytes!
Google Chrome will properly update the textarea to contain bytes 60,000-70,000. In Firefox, because the size will remain constant, it will still only show bytes in the range 40,000-50,000.
Edit: Updated the jsfiddle to demonstrate that FF can still read the updated file contents. It's just that the file size does not change with those new contents.
Edit: https://bugzilla.mozilla.org/show_bug.cgi?id=756503 (bug report)
Edit: Examples have been added & updated in response to #Eduárd Moldován's comment & #ZER0's answer. Thanks!
It seems that you're get the size property from the directly, and because the value in this form element is not changed it's likely that Firefox doesn't update the size property as well.
As workaround, you can check the length of the content you have read. So instead of file.size have evt.target.result.length.
However, it's definitely a bug to me so you have done well to add it on Bugzilla!
Update:
You can still use the string version of slice. Otherwise, if you prefer (or the result is a particular type of data), you can create from evt.target.result a new Blob object (The File Object use this interface) where you can use both size property and slice method.
I know there are many ways to prevent image caching (such as via META tags), as well as a few nice tricks to ensure that the current version of an image is shown with every page load (such as image.jpg?x=timestamp), but is there any way to actually clear or replace an image in the browsers cache so that neither of the methods above are necessary?
As an example, lets say there are 100 images on a page and that these images are named "01.jpg", "02.jpg", "03.jpg", etc. If image "42.jpg" is replaced, is there any way to replace it in the cache so that "42.jpg" will automatically display the new image on successive page loads? I can't use the META tag method, because I need everuthing that ISN"T replaced to remain cached, and I can't use the timestamp method, because I don't want ALL of the images to be reloaded every time the page loads.
I've racked my brain and scoured the Internet for a way to do this (preferrably via javascript), but no luck. Any suggestions?
If you're writing the page dynamically, you can add the last-modified timestamp to the URL:
<img src="image.jpg?lastmod=12345678" ...
<meta> is absolutely irrelevant. In fact, you shouldn't try use it for controlling cache at all (by the time anything reads content of the document, it's already cached).
In HTTP each URL is independent. Whatever you do to the HTML document, it won't apply to images.
To control caching you could change URLs each time their content changes. If you update images from time to time, allow them to be cached forever and use a new filename (with a version, hash or a date) for the new image — it's the best solution for long-lived files.
If your image changes very often (every few minutes, or even on each request), then send Cache-control: no-cache or Cache-control: max-age=xx where xx is the number of seconds that image is "fresh".
Random URL for short-lived files is bad idea. It pollutes caches with useless files and forces useful files to be purged sooner.
If you have Apache and mod_headers or mod_expires then create .htaccess file with appropriate rules.
<Files ~ "-nocache\.jpg">
Header set Cache-control "no-cache"
</Files>
Above will make *-nocache.jpg files non-cacheable.
You could also serve images via PHP script (they have awful cachability by default ;)
Contrary to what some of the other answers have said, there IS a way for client-side javascript to replace a cached image. The trick is to create a hidden <iframe>, set its src attribute to the image URL, wait for it to load, then forcibly reload it by calling location.reload(true). That will update the cached copy of the image. You may then replace the <img> elements on your page (or reload your page) to see the updated version of the image.
(Small caveat: if updating individual <img> elements, and if there are more than one having the image that was updated, you've got to clear or remove them ALL, and then replace or reset them. If you do it one-by-one, some browsers will copy the in-memory version of the image from other tags, and the result is you might not see your updated image, despite its being in the cache).
I posted some code to do this kind of update here.
Change the image url like this, add a random string to the querystring.
"image1.jpg?" + DateTime.Now.ToString("ddMMyyyyhhmmsstt");
I'm sure most browsers respect the Last-Modified HTTP header. Send those out and request a new image. It will be cached by the browser if the Last-Modified line doesn't change.
You can append a random number to the image which is like giving it a new version. I have implemented the similar logic and it's working perfectly.
<script>
var num = Math.random();
var imgSrc= "image.png?v="+num;
$(function() {
$('#imgID').attr("src", imgSrc);
})
</script>
I found this article on how to cache bust any file
There are many ways to force a cache bust in this article but this is the way I did it for my image:
fetch('/thing/stuck/in/cache', {method:'POST', credentials:'include'});
The reason the ?x=timestamp trick is used is because that's the only way to do it on a per image basis. That or dynamically generate image names and point to an application that outputs the image.
I suggest you figure out, server side, if the image has been changed/updated, and if so then output your tag with the ?x=timestamp trick to force the new image.
No, there is no way to force a file in a browser cache to be deleted, either by the web server or by anything that you can put into the files it sends. The browser cache is owned by the browser, and controlled by the user.
Hence, you should treat each file and each URL as a precious resource that should be managed carefully.
Therefore, porneL's suggestion of versioning the image files seems to be the best long-term answer. The ETAG is used under normal circumstances, but maybe your efforts have nullified it? Try changing the ETAG, as suggested.
Change the ETAG for the image.
See http://en.wikipedia.org/wiki/URI_scheme
Notice that you can provide a unique username:password# combo as a prefix to the domain portion of the uri. In my experimentation, I've found that inclusion of this with a fake ID (or password I assume) results in the treatment of the resource as unique - thus breaking the caching as you desire.
Simply use a timestamp as the username and as far as I can tell the server ignores this portion of the uri as long as authentication is not turned on.
Btw - I also couldn't use the tricks above with a google map marker icon caching problem I was having where the ?param=timestamp trick worked, but caused issues with disappearing overlays. Never could figure out why this was happening, but so far so good using this method. What I'm unsure of, is if passing fake credentials will have any adverse server performance affects. If anyone knows I'd be interested to know as I'm not yet in high volume production.
Please report back your results.
Since most, if not all, answers and comments here are copies of parts the question, or close enough, I shall throw my 2 cents in.
I just want to point out that even if there is a way it is going to be difficult to implement. The logic of it traps us. From a logical stance telling the browser to replace it's cached images for each changed image on a list since a certain date is ideal BUT... When would you take the list down and how would you know if everyone has the latest version who would visit again?
So my 1st "suggestion", as the OP asked for, is this list theory.
How I see doing this is:
A.) Have a list that our dynamic and manual changed image urls can be stored.
B.) Set a dead date where the catch will be reset and the list will be truncated regardless.
C.0) Check list on site entrance vs browser via i frame which could be ran in the background with a shorter cache header set to re-cache them all against the farthest date on the list or something of that nature.
C.1) Using the Iframe or ajax/xhr request I'm thinking you could loop through each image of the list refreshing the page to show a different image and check the cache against it's own modified date. So on this image's onload use serverside to decipher if it is not the last image when it is loaded go to the next image.
C.1a) This would mean that our list may need more information per image and I think the obvious one is the possible need of some server side script to adjust the headers as required by each image to minimize the footstep of re-caching changed site images.
My 2nd "suggestion" would be to notify the user of changes and direct them to clear their cache. (Carefully, remove only images and files when possible or warn them of data removal due to the process)
P.S. This is just an educated ideation. A quick theory. If/when I make it I will post the final. Probably not here because it will require server side scripting. This is at least a suggestion not mentioned in the OP's question that he say's he already tried.
It sounds like the base of your question is how to get the old version of the image out of the cache. I've had success just making a new call and specifying in the header not to pull from cache. You're just throwing this away once you fetch it, but the browser's cache should have the updated image at that point.
var headers = new Headers()
headers.append('pragma', 'no-cache')
headers.append('cache-control', 'no-cache')
var init = {
method: 'GET',
headers: headers,
mode: 'no-cors',
cache: 'no-cache',
}
fetch(new Request('path/to.file'), init)
However, it's important to recognize that this only affects the browser this is called from. If you want a new version of the file for any browser once the image is replaced, that will need to be accomplished via server configuration.
Here is a solution using the PHP function filemtime():
<?php
$addthis = filemtime('myimf.jpg');
?>
<img src="myimg.jpg?"<?= $addthis;?> >
Use the file modified time as a parameter will cause it to read from a cached version until the file has changed. This approach is better than using e.g. a random number as caching will still work if the file has not changed.
In the event that an image is re-uploaded, is there a way to CLEAR or REPLACE the previously cached image client-side? In my example above, the goal is to make the browser forget what "42.jpg" is
You're running firefox right?
Find the Tools Menu
Select Clear Private Data
Untick all the checkboxes except make sure Cache is Checked
Press OK
:-)
In all seriousness, I've never heard of such a thing existing, and I doubt there is an API for it. I can't imagine it'd be a good idea on part of browser developers to let you go poking around in their cache, and there's no motivation that I can see for them to ever implement such a feature.
I CANNOT use the META tag method OR the timestamp method, because I want all of the images cached under normal circumstances.
Why can't you use a timestamp (or etag, which amounts to the same thing)? Remember you should be using the timestamp of the image file itself, not just Time.Now.
I hate to be the bearer of bad news, but you don't have any other options.
If the images don't change, neither will the timestamp, so everything will be cached "under normal circumstances". If the images do change, they'll get a new timestamp (which they'll need to for caching reasons), but then that timestamp will remain valid forever until someone replaces the image again.
When changing the image filename is not an option then use a server side session variable and a javascript window.location.reload() function. As follows:
After Upload Complete:
Session("reload") = "yes"
On page_load:
If Session("reload") = "yes" Then
Session("reload") = Nothing
ClientScript.RegisterStartupScript(Me.GetType), "ReloadImages", "window.location.reload();", True)
End If
This allows the client browser to refresh only once because the session variable is reset after one occurance.
Hope this helps.
To replace cache for pictore you can store on server-side some version value and when you load picture just send this value instead timestamp. When your image will be changed change it`s version.
Try this code snippet:
var url = imgUrl? + Math.random();
This will make sure that each request is unique, so you will get the latest image always.
After much testing, the solution I have found in the following way.
1- I create a temporary folder to copy the images with the name adding time () .. (if the folder exists I delete content)
2- load the images from that temporary local folder
in this way I always make sure that the browser never caches images and works 100% correctly.
if (!is_dir(getcwd(). 'articulostemp')){
$oldmask = umask(0);mkdir(getcwd(). 'articulostemp', 0775);umask($oldmask);
}else{
rrmfiles(getcwd(). 'articulostemp');
}
foreach ($images as $image) {
$tmpname = time().'-'.$image;
$srcimage = getcwd().'articulos/'.$image;
$tmpimage = getcwd().'articulostemp/'.$tmpname;
copy($srcimage,$tmpimage);
$urlimage='articulostemp/'.$tmpname;
echo ' <img loading="lazy" src="'.$urlimage.'"/> ';
}
try below solutions,
myImg.src = "http://localhost/image.jpg?" + new Date().getTime();
Above solutions work for me :)
I usually do the same as #Greg told us, and I have a function for that:
function addMagicRefresh(url)
{
var symbol = url.indexOf('?') == -1 ? '?' : '&';
var magic = Math.random()*999999;
return url + symbol + 'magic=' + magic;
}
This will work since your server accepts it and you don't use the "magic" parameter any other way.
I hope it helps.
I have tried something ridiculously simple:
Go to FTP folder of the website and rename the IMG folder to IMG2. Refresh your website and you will see the images will be missing. Then rename the folder IMG2 back to IMG and it's done, at least it worked for me in Safari.