Get actual image url after redirect - javascript

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.

Related

Displaying cached image via Cache API

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;

How to Add Color to Static Mapbox Image Gotten from API

Below is the URL I pass to fetch API to get the image. It works great but the location pin is grey and I need it to be #800080 (purple). I get there's a marker-color parameter but not sure how to add it to the baseUrl string below. I've tried several different versions.
const baseUrl = `https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/geojson(%7B%22type%22%3A%22Point%22%2C%22coordinates%22%3A%5B${geoLocation.longitude}%2C${geoLocation.latitude}%5D%7D)/${geoLocation.longitude},${geoLocation.latitude},15/500x300?access_token=${MAPBOX_API_KEY}`;
Relevant documentation which gives additional parameters but no clear examples: https://docs.mapbox.com/api/maps/static-images/#overlay-options
Code:
const getUrlExtension = (url) => {
return url.split(/[#?]/)[0].split('.').pop().trim();
};
const onImageEdit = async (imgUrl) => {
const imgExt = getUrlExtension(imgUrl);
const response = await fetch(imgUrl);
const blob = await response.blob();
const file = new File([blob], 'locationImage.' + imgExt, {
type: blob.type,
});
setImage(file);
setPreviewSrc(response.url);
};
function previewLocation(event) {
event.preventDefault();
const MAPBOX_API_KEY ='xyz';
// baseUrl based on https://docs.mapbox.com/api/maps/static-images/#overlay-options
const baseUrl = `https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/geojson(%7B%22type%22%3A%22Point%22%2C%22coordinates%22%3A%5B${geoLocation.longitude}%2C${geoLocation.latitude}%5D%7D)/${geoLocation.longitude},${geoLocation.latitude},15/500x300?access_token=${MAPBOX_API_KEY}`;
onImageEdit(baseUrl);
}
Thanks in advance to anyone with Mapbox API experience!
I am unable to get the GeoJSON method to respond to the properties key too! Not sure what's going on there...
I am however able to get a pin showing in that color using the Marker method like the below.
https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/pin-s-l+800080(-87.0186,32.4055)/-87.0186,32.4055,14/500x300?access_token=YOUR_TOKEN_HERE.

Can not get AEC model Data in Autodesk Forge

i try to activate Revit Levels and 2D Minimap extension in autodesk forge viewer, but can not get AEC Model Data. I got this worning`
i tried to get AEC data with this code
const url = window.location.search;
console.log(url);
const svf_path = `${url.replace("?", "/storage/").replace(/%20/g, " ")}`;
Autodesk.Viewing.endpoint.getItemApi = (endpoint, derivativeUrn, api) => {
return svf_path;
};
Autodesk.Viewing.Initializer(options, async () => {
const paths = svf_path.split("/");
const [dest, svf_dir] = [paths[2], paths[3]];
const url = `/api/viewer/dest/${dest}/svf/${svf_dir}/manifest`;
const response = await fetch(url);
const manifest = await response.json();
const init_div = document.getElementById("init_div");
viewer = new Autodesk.Viewing.GuiViewer3D(init_div, config3d);
const viewerDocument = new Autodesk.Viewing.Document(manifest);
const viewable = viewerDocument.getRoot().getDefaultGeometry();
viewer.start();
await viewerDocument.downloadAecModelData();
viewer.loadDocumentNode(viewerDocument, viewable)
.then(function (result) {
Autodesk.Viewing.Document.getAecModelData(viewable);
})
});
wats wrong in my code?
The warning comes from the BubbleNode.prototype.getAecModelData method. You are not calling it in your code but it's possible that it's being called by the LevelsExtension itself. Try configuring the extension so that it doesn't detect the AEC data automatically by passing in { autoDetectAecModelData: false } as the extension options.
Btw. to debug the issue on your side, you can also try getting the non-minified version of viewer3D.js, put a breakpoint to where the warning is being logged, and see the call stack when the breakpoint is hit.

How to check if a URL is same origin as current page and/or worker

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>

How to check whether it is a blob url using javascript

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')

Categories