I'm trying to use the Cache API to store large files (one being 500MB) for offline use. I'm able to create/open the cache, fetch the image URL from AWS S3, and put it into the cache:
let cache = await caches.open('my-cache');
let url = 'https://[mybucket].s3.amazonaws.com/example.png';
await fetch(url).then( async (response) => {
if (!response.ok) {
throw new TypeError("Bad response status");
}
return cache.put(url, response);
});
This appears properly in the inspector with (what I think) are the correct CORS/content size responses:
I'm also able to find it in the cache, however instead of searching for the full S3 url it seems to only store it with /example.png as the match:
const options = {
ignoreSearch: true,
ignoreMethod: true,
ignoreVary: true
};
let img = await cache.match('/example.png', options);
This returns a ReadableStream successfully, but I don't know where to go next.
I am hoping to default to loading from the cache since the file sizes can get quite large.
Checking the documentation for Cache.match(), what you're getting in response should be - well, a Response! So, from there, you can use Response.blob() and pass that into URL.createObjectURL(), which you can make the src of your image.
That was a mouthful, so here it is in code.
// Get the cached item...
const options = {
ignoreSearch: true,
ignoreMethod: true,
ignoreVary: true
};
let img = await cache.match('/example.png', options);
// Handle the possibility of undefined
if(img === undefined) { return false; }
// Convert the image data to a blob...
let blob = img.blob();
// Assuming imgElement is our image element...
imgElement.src = URL.createObjectURL(blob);
// And finish it all off nicely.
return true;
Related
I'm working with the docusign api in order to get some documents from envelopes, I get all the info, but for the PDF download I get a "filebytes" string, and when trying to process it to download it, I get just a blank page (not sure if that's the expecting result since I'm using sandbox account). I'm doing all this from the client.
here's what I'm doing to process the string:
const pdfBlob = new Blob([Buffer.from(content)], {
type: 'application/pdf'
});
if (window.navigator && window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(pdfBlob, filename);
resolve();
} else {
const tempLink = document.createElement('a');
const url = window.URL.createObjectURL(pdfBlob);
const clickEvent = new MouseEvent('click', {
'view': window,
'bubbles': true,
'cancelable': false
});
tempLink.href = url;
tempLink.target = '_blank';
tempLink.download = filename;
document.body.appendChild(tempLink);
tempLink.dispatchEvent(clickEvent);
setTimeout(() => {
document.body.removeChild(tempLink);
window.URL.revokeObjectURL(url);
resolve();
}, 100);
}
});
Any ideas?
Here is a blog post on this topic.
The Node.js code is this (you have to do this from server):
// You would need to obtain an accessToken using your chosen auth flow
let apiClient = new ApiClient(basePath);
let config = new docusign.Configuration(apiClient);
config.addDefaultHeader('Authorization', 'Bearer ' + accessToken);
let envelopesApi = new docusign.EnvelopesApi(config);
var accountId; // accountId for your DocuSign account
var envelopeId; // envelopeId that you are working on
// produce a ZIP file with all documents including the CoC
let results1 = await envelopesApi.GetDocument(accountId, envelopeId, 'archive', null);
// produce a PDF combining all signed documents as well as the CoC
let results2 = await envelopesApi.GetDocument(accountId, envelopeId, 'combined', null);
// produce a particular document with documentId '1'
let results3 = await envelopesApi.GetDocument(accountId, envelopeId, '1', null);
//TODO - use byte array to write or send the file for your use
If your code or this code returns an empty page, please confirm that you don't get it if you use the DocuSign web app, it's possible it is empty?
Please remember encoding, this is using 64 bit encoding to get bits in the REST API.
There's a similar question here
javascript How to check if a URL is same origin as current page?
Unfortunately it doesn't specify that answers must work in both a page and a worker so none of the answers work in a worker.
In particular I'm trying to use the fetch API to fetch images in a way that works as automatically as possible. When an image is fetched I want to set the mode to cors but only if the URL is not the same origin. I need to know how to do that in both a page context and a worker context.
Pseudo code
function loadImageBitmap(url) {
const options = {};
if (urlIsNotSameOrigin(url)) {
options.mode = 'cors';
}
return fetch(url, options)
.then((response) => {
if (!response.ok) {
throw response;
}
return response.blob();
}).then((blob) => {
return global.createImageBitmap(blob);
});
}
function urlIsNotSameOrigin(url) {
// what do I put here?
}
// examples
async function main() {
const bitmaps = Promise.all([
'https://notsamedomain.com/foo.jpg',
'../relative/image.png',
'/absolute/thing.jpg',
'//other/absolute/thing.jpg',
'https://samedomain.com/bar.gif',
].map(loadImageBitmap));
}
Most of the solutions I've seen are to make an anchor <a> element, assign the src property and then read it. But anchors don't exist in workers. The URL object doesn't seem to handle relative resources like the anchor does.
The difference between the URL() constructor and the anchor <a> element is that the URL() constructor is not a Node nor is it attached to a particular Document object, and thus doesn't have a baseURI it can hook on.
So what you need in order to make the URL() constructor behave the same as an anchor <a> element is to pass a baseURI as the second argument of the constructor.
This means that in a document new URL(uri, document.baseURI) would return the same URL properties as Object.assign(document.createElement('a'), {href: uri}), as long as the URI produced is a valid one.
Now, in a Worker we still don't have access to a Node's baseURI either, and it's quite unclear which you want to hook on.
In most cases, you can simply use self.location.href as a base URI, and that might actually be what you want if you are going to fetch same-origin resources, but if you do initialize your Worker from a blobURL, you may have to pass it from the main scope, just like I had to do in StackSnippetĀ®'s over-protected iframe.
// init worker from a blobURI...
const worker_url = getWorkerURL(worker_script);
const worker = new Worker(worker_url);
worker.onmessage = e => console.log(e.data);
worker.postMessage({
// we pass the base URL
base: 'https://samedomain.com/foo.html',
uris: [
'https://notsamedomain.com/foo.jpg',
'../relative/image.png',
'/absolute/thing.jpg',
'//other/absolute/thing.jpg',
'https://samedomain.com/bar.gif'
]
});
//__MISC__________
// gets our Worker's blobURL based on a Blob made
// from a <script> element textContent
function getWorkerURL(el) {
const content = el.textContent;
const blob = new Blob([content], {type: 'application/javascript'});
return URL.createObjectURL(blob);
}
<script id="worker_script" type="worker_script">
onmessage = e => {
const d = e.data;
// if we weren't in a null origined iframe's blobURI we could do
//const self_url = new URL(location.href)
// but here we pass the fake base domain
const self_url = new URL(d.base);
const sameorigins = d.uris.filter( uri => {
try { // wrap in a try-catch, invalids throw
const url = new URL(uri, self_url);
return url.origin === self_url.origin;
} catch(e) { return false; }
})
postMessage(sameorigins);
};
</script>
I have a situation where I am converting blobURL to base64 dataURLs, but I want to do this only if url is a blobURL.
So is there any way to check whether it is valid blob url?
my blob url - blob:http://192.168.0.136/85017e84-0f2d-4791-b563-240794abdcbf
You are facing an x-y problem.
You absolutely don't need to check if your blobURI is a valid one, because you absolutely don't need to use the blobURI in order to create a base64 version of the Blob it's pointing to.
The only way to do it is to fetch the Blob and this means creating a copy of its data in memory for no-good.
What you need is a way to retrieve this Blob.
There is unfortunately no official way to do so with the web APIs, but it's not that hard to make it ourselves:
We simply have to overwrite the default URL.createObjectURL method in order to map the passed Blob in a dictionnary using the blobURI as key:
(() => {
// overrides URL methods to be able to retrieve the original blobs later on
const old_create = URL.createObjectURL;
const old_revoke = URL.revokeObjectURL;
Object.defineProperty(URL, 'createObjectURL', {
get: () => storeAndCreate
});
Object.defineProperty(URL, 'revokeObjectURL', {
get: () => forgetAndRevoke
});
Object.defineProperty(URL, 'getBlobFromObjectURL', {
get: () => getBlob
});
const dict = {};
function storeAndCreate(blob) {
var url = old_create(blob); // let it throw if it has to
dict[url] = blob;
return url
}
function forgetAndRevoke(url) {
old_revoke(url);
// some checks just because it's what the question titel asks for, and well to avoid deleting bad things
try {
if(new URL(url).protocol === 'blob:')
delete dict[url];
}catch(e){} // avoided deleting some bad thing ;)
}
function getBlob(url) {
return dict[url];
}
})();
// a few example uses
const blob = new Blob(['foo bar']);
// first normal use everyhting is alive
const url = URL.createObjectURL(blob);
const retrieved = URL.getBlobFromObjectURL(url);
console.log('retrieved: ', retrieved);
console.log('is same object: ', retrieved === blob);
// a revoked URL, of no use anymore
const revoked = URL.createObjectURL(blob);
URL.revokeObjectURL(revoked);
console.log('revoked: ', URL.getBlobFromObjectURL(revoked));
// an https:// URL
console.log('https: ', URL.getBlobFromObjectURL(location.href));
PS: for the ones concerned about the case a Blob might be closed (e.g user provided file has been deleted from disk) then simply listen for the onerror event of the FileReader you'd use in next step.
you could do something like
var url = 'blob:http://192.168.0.136/85017e84-0f2d-4791-b563-240794abdcbf';
if(url.search('blob:') == -1){
//do something
}
you may also use reg-expression based check with url.match('url expression')
Hi Unsplash allows to load random image from their website via:
https://source.unsplash.com/random/2560x1440
if I access the url from the browser each time the url generates random static image eg:
https://images.unsplash.com/photo-1488616268114-d949a6d72edf?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=2560&h=1440&fit=crop&s=a7f66c8417bcf79d2503b84db64c7b1a
I would like to request the image in jquery or js via the first url and in response get the second one. Is this possible?
You can use the responseURL of the XMLHttpRequest.
MDN Reference
and this answer for an example in jQuery and native JS.
This is tricky since there are several things going on.
If you use Chrome or Firefox, open the developer tools and review the network tab you will see that the original request returns an HTTP 302 redirect to a different URL:
The browser then follows the specified Location header and you get the page that you see. The image is within an <img> on that page.
So to get the final image you need to:
Do an ajax request to the initial URL
Parse the response headers to get the new location
Do an ajax request to the new location
Parse the HTML in the new response to grab the actual image URL out of the img tag.
Good luck!
You can use PerformanceObserver to get the name property value of the requested resource
const key = "unsplash";
const url = `https://source.${key}.com/random/2560x1440`;
let bloburl = void 0;
let img = new Image;
const getResourceName = () => {
let resource = "";
const observer = new PerformanceObserver((list, obj) => {
// alternatively iterate all entries, check `"name"`
// property value for `key`, break loop if specific resource requested
for (let entry of list.getEntries()) {
let {name: res} = entry.toJSON();
resource = res;
}
});
observer.observe({
entryTypes: ["resource"]
});
return fetch(url)
.then(response => response.blob())
.then(blob => {
observer.disconnect();
bloburl = URL.createObjectURL(blob);
img.src = bloburl;
document.body.appendChild(img);
return resource
})
}
getResourceName().then(res => console.log(res)).catch(err => console.log(err))
You can alternatively use Response.url
const key = "unsplash";
const url = `https://source.${key}.com/random/2560x1440`;
let bloburl = void 0;
let img = new Image;
const getResourceName = fetch(url)
.then(response => Promise.all([response.url, response.blob()]))
.then(([resource, blob]) => {
bloburl = URL.createObjectURL(blob);
img.src = bloburl;
document.body.appendChild(img);
return resource
});
getResourceName.then(res => console.log(res)).catch(err => console.log(err))
fetch('https://source.unsplash.com/random').then(res=>{console.log(res)})
from source worked for me.
For example i want to load 100MB mp3 file into AudioContext, and i can do that with using XMLHttpRequest.
But with this solution i need to load all file and only then i can play it, because onprogress method don't return data.
xhr.onprogress = function(e) {
console.log(this.response); //return null
};
Also i tried to do that with fetch method, but this way have same problem.
fetch(url).then((data) => {
console.log(data); //return some ReadableStream in body,
//but i can't find way to use that
});
There is any way to load audio file like stream in client JavaScript?
You need to handle the ajax response in a streaming way.
there is no standard way to do this until fetch & ReadableStream have properly been implemented across all the browsers
I'll show you the most correct way according to the new standard how you should deal with streaming a ajax response
// only works in Blink right now
fetch(url).then(res => {
let reader = res.body.getReader()
let pump = () => {
reader.read().then(({value, done}) => {
value // chunk of data (push chunk to audio context)
if(!done) pump()
})
}
pump()
})
Firefox is working on implementing streams but until then you need to use xhr and moz-chunked-arraybuffer
IE/edge has ms-stream that you can use but it's more complicated
How can I send value.buffer to AudioContext?
This only plays the first chunk and it doesn't work correctly.
const context = new AudioContext()
const source = context.createBufferSource()
source.connect(context.destination)
const reader = response.body.getReader()
while (true) {
await reader.read()
const { done, value } = await reader.read()
if (done) {
break
}
const buffer = await context.decodeAudioData(value.buffer)
source.buffer = buffer
source.start(startTime)
}