Pass parameter to BLOB object URL - javascript

Say I've got a reference to a html file as a Blob b and I create a URL for it, url = URL.createObjectURL(b);.
This gives me something that looks like blob:http%3A//example.com/a0440b61-4850-4568-b6d1-329bae4a3276
I then tried opening this in an <iframe> with a GET parameter ?foo=bar, but it didn't work. How can I pass the parameter?
var html ='<html><head><title>Foo</title></head><body><script>document.body.textContent = window.location.search<\/script></body></html>',
b = new Blob([html], {type: 'text/html'}),
url = URL.createObjectURL(b),
ifrm = document.createElement('iframe');
ifrm.src = url + '?foo=bar';
document.body.appendChild(ifrm);
// expect to see ?foo=bar in <iframe>
DEMO

I don't think adding a query string to the url will work as it essentially changes it to a different url.
However if you simply want to pass parameters you can use the hash to add a fragment to the url
ifrm.src = url + '#foo=bar';
http://jsfiddle.net/thpf584n/1/

For completeness sake, if you want to be able to reference a blob that has as question mark "query string" indicator in it, you can do so in Firefox any way you choose, such as: blob:lalalal?thisworksinfirefox
For Chrome, the above will not work, but this will: blob:lalalla#?thisworksinchromeandfirefox
And for Safari and Microsaft, nothing really works, so do a pre test like so, then plan accordingly:
function initScriptMode() {
var file = new Blob(["test"], {type: "text/javascript"});
var url = URL.createObjectURL(file) + "#test?test";
var request = new XMLHttpRequest();
request.responseType = responseType || "text";
request.open('GET', url);
request.onload = function() {
alert("you can use query strings")
};
try {
request.send();
}
catch(e) {
alert("you can not use query strings")
}
}

If you are doing this with a Javascript Blob for say a WebWorker then you can just to add the parameters into the Blob constructor as a global variable:
const parameters = 'parameters = ' + JSON.stringify({foo:'bar'});
const body = response.body; // From some previous HTTP request
const blob = new Blob([parameters, body], { type: 'application/javascript' });
new Worker(URL.createObjectURL(blob));
Or more general case just store the original URL on the location object
const location = 'location.originalHref = "' + url + '";';
const body = response.body; // From some previous HTTP request
const blob = new Blob([location, body], { type: 'application/javascript' });
new Worker(URL.createObjectURL(blob));
You could also do this with HTML if you can add them say to the root <HTML> tag as attributes or use the <BASE> element for the url or insert them as a script tag but this would require you to modify the response HTML rather then just prepend some extra data

Related

URL transformation in Chrome javascript bookmarklet showing "undefined"

I'm trying to create a Chrome bookmarklet that will take a part of the pathname from one URL and navigate to a new URL using that variable as a parameter (the variable is 1234567 in the example below).
From: 'https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115'
To: 'https://example.com/tool/permissions/resources?namespace=1234567'
The hostname differs depending on the environment I'm working in but will always stay the same when I transform it with the bookmarklet so I'm trying to pull that info when I compose the new URL. This is what I've got so far, but I keep getting "undefined" in the transformed URL (below) when I run the code. Any ideas on what I've got wrong here?
'https://example.com/tool/permissions/resources?namespace=undefined'
My code:
//Sample URL: https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115
var pathArray = location.pathname.split('/');
let secondLevelLocation = pathArray[3];
var newUrl = location.protocol + '//' + location.hostname + '/tool/permissions/resources?namespace=' + secondLevelLocation;
var w=window.open();w.location=newUrl;w.document.close();
In the code you've shown, there is an assumption that the URL hash (fragment identifier) will be included when accessing the pathname:
//Sample URL: https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115
var pathArray = location.pathname.split('/');
let secondLevelLocation = pathArray[3];
This is where the problem occurs. In a URL, the pathname ends when one of the following characters are first encountered:
? (which begins the query string), or
# (which begins the fragment identifier)
The format of the hash / fragment identifier portion of the URL in your example is that of a fully-resolved URL without the origin (starting at the pathname).
Using this knowledge, you can use the native URL class to help you select the desired part of the input URL, then use it again to construct the target URL, as shown in the code below. Once you have the target URL, you can use it to navigate, etc.
function parseNamespace (url) {
const fragment = url.hash.slice(1);
if (!fragment.startsWith('/')) throw new Error('Path fragment not found');
url = new URL(fragment, url);
const namespace = url.pathname.split('/').at(-1);
return namespace;
}
function createUrl (address = window.location.href) {
let url = new URL(address);
const namespace = parseNamespace(url);
const pathname = '/tool/permissions/resources';
url = new URL(pathname, url.origin);
url.searchParams.set('namespace', namespace);
return url;
}
const url = createUrl('https://example.com/reporting-dashboard/#/dashboard/1234567?pageId=Page_3a7c73c6-34c9-4ab3-8d1f-5bd437c07115');
// You can omit the argument when you want to get the address from the current document:
// const url = createUrl();
console.log(url.href); // "https://example.com/tool/permissions/resources?namespace=1234567"

How to get "file" parameter of http request in javascript?

I'm trying to program a java script script that based on whether a user logs in properly or not will redirect them to a separate PHP script. The issue is that I can't seem to figure out how to get the file parameter of the request so that I can see if the request I'm looking for is there. How do I get the file parameter of a request in java script?
Sorry for misconceptions, what i mean by the file attribute is what is under the "file" section for each request in the following.
example
So if under the file tab of the packet, it set a certain file, how would i differentiate?
It's not clear what you're asking.
The part " so that I can see if the request I'm looking for is there" tells me, you want to debug your website, or at least, that's my interpretation of it.
If you use Chrome or Firefox Developer Edition, you can press F12 (or CTRL + SHIFT + J) to open the developer console.
Change to the tab "Network, and you'll see all the XMLHTTPRequests.
Click on a specific request, and you'll see its details.
A basic XmlHttpReuqest goes like this:
function reqListener () {
console.log(this.responseText);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "http://www.example.org/example.txt");
oReq.send();
And you get the result of your request in the callback function reqListener.
See also https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
If you want to get the request handler's URL, that goes like this:
function reqListener (e) {
//console.log(this.responseText);
console.log(e);
console.log(e.currentTarget.responseURL);
}
var oReq = new XMLHttpRequest();
oReq.addEventListener("load", reqListener);
oReq.open("GET", "https://stackoverflow.com/questions/58407228");
oReq.send();
And if you want to get a parameter called "file" inside an url, this goes like
function getUrlVars(urlHref)
{
var vars = [], hash;
var hashes = urlHref.slice(urlHref.indexOf('?') + 1).split('&');
var i;
for (i = 0; i < hashes.length; i++)
{
hash = hashes[i].split('=');
vars.push(decodeURIComponent(hash[0]));
vars[decodeURIComponent(hash[0])] = decodeURIComponent(hash[1]);
} // Next i
return vars;
} // End Function getUrlVars
var dictParameters = getUrlVars("http://www.example.com/handler?file=bla.bin");
if (dictParameters.contains("file"))
{
console.log(dictParameters["file"]);
}
As for XMLHTTPRequest, it doesn't have a property called file.
Also, this is 2019, you should be using the FETCH-API with async and await, not the XMLHttpRequest-API, which doesn't use promises.
Here's a getting started overview.
Edit:
Ah, I see:
If you have a url, such as
var url = "http://www6.scratch99.com/web-development/javascript/test.js?abc=def";
you do
var url = "http://www6.scratch99.com";
var urlParts = url.replace('http://','').replace('https://','').split(/[/?#]/);
var domain = urlParts[0];
to get the domain part. Then you subtract the domain (+protocol), and end it at ? or #:
Full code:
var url = "http://www6.scratch99.com/web-development/javascript/test.js?abc=def";
// var url = "http://www6.scratch99.com";
// var url = "http://www6.scratch99.com?test=123";
var protocol = url.substr(0, url.indexOf(":") + 3)
var urlParts = url.substr(protocol.length).split(/[/?#]/);
var domain = urlParts[0];
var fileParts = url.substr(protocol.length + domain.length);
var file = fileParts.split(/[?#]/)[0];
and if you want the filename only:
var pathParts = file.split('/');
var fileOnly = pathParts[pathParts.length-1];

How to get response value from fetch instead of a promise with no value?

I am building a chrome extension to pull data out of a page to build a url from the data and I want to have that url shortened as the final product. In my content scripts file I make a call out to a url shortener to compress a link. I keep getting returned a promise with no value which crashes react. In devtools I see that the callout is made successfully and the url is returned.
I have tried async await, a full async function, tried forcing the response.toString()
Here is the relevant section of code.
var listingInfo = new Map();
listingInfo.set('Address', 'some standard address');
var tinyLink = '(http://tinyurl.com)/api-create.php?url='; //() because I can't share shortener urls on this site.
/*-----------------------------------GET LINKS--------------------------*/
if(listingInfo.has('Address')){ var mapsLink = \`https://www.google.com/maps/place/${listingInfo.get('Address').replace(new RegExp(" ", "g"), '+')}\`;
tinyLink = \`${tinyLink}${mapsLink} `;
var dirLink = fetch(tinyLink, {
method: "GET",
mode: "no-cors",
headers: {"Content-Type": "text/html"} }).then((response)=>{
return response; });
listingInfo.set('dirLink', dirLink); }
I expected to receive a plain text string because in the network tab of devtools it shows a simple string url and not any JSON, but I keep receiving a resolved promise with value="".
// made this function to use XMLHttpRequest()
const setLink = (propName, url) => {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var link = xhr.responseText;
console.log(`link: '${link}'`);
listingInfo.set(propName, link);
}
}
xhr.send();
}
// Then called setLink()
if(listingInfo.has('Address')){
var mapsLink =
`https://www.google.com/maps/place/${listingInfo.get('Address').replace(new
RegExp(" ", "g"), '+')}`;
dirLink = `${tinyLink}${mapsLink}`;
console.log(dirLink);
setLink('dirLink', dirLink);
console.log(dirLink);
}

Why does AJAX output comes with wrong encoding?

I'm getting a file from a server with AJAX (Angular).The file is a simple XLSX document, sent like this:
ob_start();
$file = \PHPExcel_IOFactory::createWriter($xls, 'Excel2007');
$file->save('php://output');
$response->setContent(ob_get_clean());
$response->headers->replace(array(
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition' => 'attachment;filename=file.xlsx"'
));
When I make a request from frontend, I use Accept header too. Then I save the file with angular-file-saver using FileSaver.js and Blob.js.
But the received file is corrupt and I can't open it in Excel: it's size is (for example) 12446 bytes, but Chrome's DevTools Network tab shows responses Content-Length header as 7141 bytes.
How can I solve this problem?
UPD:
I'm sending a request like this:
$http.get(baseURL + '/' + entity + '/export/?' + condition + sort, {
headers: {'Accept': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8'}
});
and downloading file just like this:
var data = new Blob([response.data], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'});
FileSaver.saveAs(data, 'file.xlsx');
The way I got around the problem was using plain JS AJAX, instead of AngularJS. (There might be a problem with AngularJS and JQuery handling binary responses.)
This should work:
var request = new XMLHttpRequest();
request.open('GET', 'http://yourserver/yourpath', true);
request.responseType = 'blob';
request.onload = function (e) {
if (this.status === 200) {
var blob = this.response;
if (window.navigator.msSaveOrOpenBlob) {
var fileNamePattern = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
window.navigator.msSaveBlob(blob, fileNamePattern.exec(request.getResponseHeader("content-disposition"))[1]);
} else {
var downloadLink = window.document.createElement('a');
var contentTypeHeader = request.getResponseHeader("Content-Type");
var b = new Blob([blob], { type: contentTypeHeader });
downloadLink.href = window.URL.createObjectURL(b);
var fileNamePattern = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
downloadLink.download = fileNamePattern.exec(request.getResponseHeader("content-disposition"))[1];
document.body.appendChild(downloadLink);
downloadLink.click();
document.body.removeChild(downloadLink);
window.URL.revokeObjectURL(b);
}
}
};
request.send();
Code is based on this and this.
FYI, I found that new Blob([response.data], ...) returns almost double the size of response.data when response.data is not returned as blob, but text/plain or application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. To get around it, you need to pass it an array of bytes instead:
var i, l, d, array;
d = this.result;
l = d.length;
array = new Uint8Array(l);
for (var i = 0; i < l; i++){
array[i] = d.charCodeAt(i);
}
var b = new Blob([array], {type: 'application/octet-stream'});
window.location.href = URL.createObjectURL(b);
Code is from here.
Anyways, since the AJAX response is not correct using AngularJS, you won't get a valid xlsx file this way. You need to go with vanilla JS.

Generate a blob with grunt which will be available to JS in var

I need to embed a Flash .swf on the page and am unable use the normal way of setting the src or data attribute to the swf url - don't ask :s. So, I'm doing an ajax request for the swf, converting to a blob and then generating a blob url which I set as the swf src. Then I realised that as I'm building with Grunt, there may be a way to just write the swf file into the code as a blob in a var, and avoid the ajax request completely. Here's the code with the ajax request:
function createFlashMovie(blobUrl){
var obj = document.createElement("object");
obj.setAttribute("width", "800");
obj.setAttribute("height", "600");
obj.setAttribute("type", "application/x-shockwave-flash");
obj.setAttribute("data", blobUrl);
document.body.appendChild(obj);
}
function onAjaxLoad(oResponse){
blobUrl = window.URL.createObjectURL(oResponse);
createFlashMovie(blobUrl);
};
//do the xhr request for a.swf
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function(){
if (this.readyState == 4 && this.status == 200){
onAjaxLoad(this.response);
}
}
xhr.open('GET', '//theserver.com/a.swf');
xhr.responseType = 'blob';
xhr.send();
...but I'm sure it must be possible to have something like this which is replaced by grunt to have the blob already available when it runs, and go straight to creating the blob url without the xhr request:
var theBlob = new Blob(["GRUNT_WRITES_THIS_IN_FROM_FILE"], {type: "application/x-shockwave-flash"});
Well, grunt is at its core just a Node program, so you can use any node command in your Gruntfile or tasks definitions. And it seems that Node's http.request would be perfect for your needs.
So:
add a custom task in your Gruntfile (http://gruntjs.com/creating-tasks#custom-tasks)
that uses http.request to download your swf (https://docs.nodejitsu.com/articles/HTTP/clients/how-to-create-a-HTTP-request)
update your code to use the local swf
I found a solution. Convert your swf file to be a base64-encoded string. At the moment I'm doing this separately and then pasting it into the source JS, but it can be automated at build time with grunt. Then in the page script create a var to store it as a dataURI:
var swfAsDataUri = "data:application/x-shockwave-flash;base64,BIG_LONG_CHUNK_OF_DATA_THAT_IS_YOUR_ENCODED_SWF_FILE__GRUNT_CAN_WRITE_THIS_IN_AT_BUILD_TIME";
Create a blob from the data url, and then create an object url from the blob:
//function taken from http://stackoverflow.com/questions/27159179/how-to-convert-blob-to-file-in-javascript
dataURLToBlob = function(dataURL) {
var BASE64_MARKER = ';base64,';
var parts = dataURL.split(BASE64_MARKER);
var contentType = parts[0].split(':')[1];
var raw = window.atob(parts[1]);
var rawLength = raw.length;
var uInt8Array = new Uint8Array(rawLength);
for (var i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], {type: contentType});
};
var blobUrl = window.URL.createObjectURL( dataURLToBlob(swfAsDataUri) );
You can then use the object url as the src data for the flash movie's object tag when it's embedded:
function createFlashMovie(blobUrl){
var obj = document.createElement("object");
obj.setAttribute("width", "800");
obj.setAttribute("height", "600");
obj.setAttribute("type", "application/x-shockwave-flash");
obj.setAttribute("data", blobUrl); //use the object url here
document.body.appendChild(obj);
}
...and there you have it, no additional http request for the swf file.

Categories

Resources