How do I find the file extension of a URL using javascript?
example URL:
http://www.adobe.com/products/flashplayer/include/marquee/design.swf?width=792&height=294
I just want the 'swf' of the entire URL.
I need it to find the extension if the url was also in the following format
http://www.adobe.com/products/flashplayer/include/marquee/design.swf
Obviously this URL does not have the parameters behind it.
Anybody know?
Thanks in advance
function get_url_extension( url ) {
return url.split(/[#?]/)[0].split('.').pop().trim();
}
example:
get_url_extension('https://example.com/folder/file.jpg');
get_url_extension('https://example.com/fold.er/fil.e.jpg?param.eter#hash=12.345');
outputs ------> jpg
Something like this maybe?
var fileName = 'http://localhost/assets/images/main.jpg';
var extension = fileName.split('.').pop();
console.log(extension, extension === 'jpg');
The result you see in the console is.
jpg true
if for some reason you have a url like this something.jpg?name=blah or something.jpg#blah then you could do
extension = extension.split(/\#|\?/g)[0];
drop in
var fileExtension = function( url ) {
return url.split('.').pop().split(/\#|\?/)[0];
}
For the extension you could use this function:
function ext(url) {
// Remove everything to the last slash in URL
url = url.substr(1 + url.lastIndexOf("/"));
// Break URL at ? and take first part (file name, extension)
url = url.split('?')[0];
// Sometimes URL doesn't have ? but #, so we should aslo do the same for #
url = url.split('#')[0];
// Now we have only extension
return url;
}
Or shorter:
function ext(url) {
return (url = url.substr(1 + url.lastIndexOf("/")).split('?')[0]).split('#')[0].substr(url.lastIndexOf("."))
}
Examples:
ext("design.swf")
ext("/design.swf")
ext("http://www.adobe.com/products/flashplayer/include/marquee/design.swf")
ext("/marquee/design.swf?width=792&height=294")
ext("design.swf?f=aa.bb")
ext("../?design.swf?width=792&height=294&.XXX")
ext("http://www.example.com/some/page.html#fragment1")
ext("http://www.example.com/some/dynamic.php?foo=bar#fragment1")
Note:
File extension is provided with dot (.) at the beginning. So if result.charat(0) != "." there is no extension.
This is the answer:
var extension = path.match(/\.([^\./\?]+)($|\?)/)[1];
Take a look at regular expressions. Specifically, something like /([^.]+.[^?])\?/.
// Gets file extension from URL, or return false if there's no extension
function getExtension(url) {
// Extension starts after the first dot after the last slash
var extStart = url.indexOf('.',url.lastIndexOf('/')+1);
if (extStart==-1) return false;
var ext = url.substr(extStart+1),
// end of extension must be one of: end-of-string or question-mark or hash-mark
extEnd = ext.search(/$|[?#]/);
return ext.substring (0,extEnd);
}
url.split('?')[0].split('.').pop()
usually #hash is not part of the url but treated separately
This method works fine :
function getUrlExtension(url) {
try {
return url.match(/^https?:\/\/.*[\\\/][^\?#]*\.([a-zA-Z0-9]+)\??#?/)[1]
} catch (ignored) {
return false;
}
}
You can use the (relatively) new URL object to help you parse your url. The property pathname is especially useful because it returns the url path without the hostname and parameters.
let url = new URL('http://www.adobe.com/products/flashplayer/include/marquee/design.swf?width=792&height=294');
// the .pathname method returns the path
url.pathname; // returns "/products/flashplayer/include/marquee/design.swf"
// now get the file name
let filename = url.pathname.split('/').reverse()[0]
// returns "design.swf"
let ext = filename.split('.')[1];
// returns 'swf'
var doc = document.location.toString().substring(document.location.toString().lastIndexOf("/"))
alert(doc.substring(doc.lastIndexOf(".")))
const getUrlFileType = (url: string) => {
const u = new URL(url)
const ext = u.pathname.split(".").pop()
return ext === "/"
? undefined
: ext.toLowerCase()
}
function ext(url){
var ext = url.substr(url.lastIndexOf('/') + 1),
ext = ext.split('?')[0],
ext = ext.split('#')[0],
dot = ext.lastIndexOf('.');
return dot > -1 ? ext.substring(dot + 1) : '';
}
If you can use npm packages, File-type is another option.
They have browser support, so you can do this (taken from their docs):
const FileType = require('file-type/browser');
const url = 'https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';
(async () => {
const response = await fetch(url);
const fileType = await FileType.fromStream(response.body);
console.log(fileType);
//=> {ext: 'jpg', mime: 'image/jpeg'}
})();
It works for gifs too!
Actually, I like to imporve this answer, it means my answer will support # too:
const extExtractor = (url: string): string =>
url.split('?')[0].split('#')[0].split('.').pop() || '';
This function returns the file extension in any case.
If you wanna use this solution. these packages are using latest import/export method.
in case you wanna use const/require bcz your project is using commonJS you should downgrade to older version.
i used
"got": "11.8.5","file-type": "16.5.4",
const FileType = require('file-type');
const got = require('got');
const url ='https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg';
(async () => {
const stream = got.stream(url);
console.log(await FileType.fromStream(stream));
})();
var fileExtension = function( url ) {
var length=url.split(?,1);
return length
}
document.write("the url is :"+length);
Related
I like to write a Thunderbird AddOn that encrypts stuff. For this, I already extracted all data from the compose window. Now I have to save this into files and run a local executable for encryption. But I found no way to save the files and execute an executable on the local machine. How can I do that?
I found the File and Directory Entries API documentation, but it seems to not work. I always get undefined while trying to get the object with this code:
var filesystem = FileSystemEntry.filesystem;
console.log(filesystem); // --> undefined
At least, is there a working AddOn that I can examine to find out how this is working and maybe what permissions I have to request in the manifest.json?
NOTE: Must work cross-platform (Windows and Linux).
The answer is, that WebExtensions are currently not able to execute local files. Also, saving to some local folder on the disk is also not possible.
Instead, you need to add some WebExtension Experiment to your project and there use the legacy APIs. There you can use the IOUtils and FileUtils extensions to reach your goal:
Execute a file:
In your background JS file:
var ret = await browser.experiment.execute("/usr/bin/executable", [ "-v" ]);
In the experiment you can execute like this:
var { ExtensionCommon } = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
var { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["IOUtils");
async execute(executable, arrParams) {
var fileExists = await IOUtils.exists(executable);
if (!fileExists) {
Services.wm.getMostRecentWindow("mail:3pane")
.alert("Executable [" + executable + "] not found!");
return false;
}
var progPath = new FileUtils.File(executable);
let process = Cc["#mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
process.init(progPath);
process.startHidden = false;
process.noShell = true;
process.run(true, arrParams, arrParams.length);
return true;
},
Save an attachment to disk:
In your backround JS file you can do like this:
var f = messenger.compose.getAttachmentFile(attachment.id)
var blob = await f.arrayBuffer();
var t = await browser.experiment.writeFileBinary(tempFile, blob);
In the experiment you can then write the file like this:
async writeFileBinary(filename, data) {
// first we need to convert the arrayBuffer to some Uint8Array
var uint8 = new Uint8Array(data);
uint8.reduce((binary, uint8) => binary + uint8.toString(2), "");
// then we can save it
var ret = await IOUtils.write(filename, uint8);
return ret;
},
IOUtils documentation:
https://searchfox.org/mozilla-central/source/dom/chrome-webidl/IOUtils.webidl
FileUtils documentation:
https://searchfox.org/mozilla-central/source/toolkit/modules/FileUtils.jsm
I am using library to parse urls on my page:
import * as urlParse from 'url-parse';
const parseUrl = url => {
try {
return urlParse(url);
} catch (e) {
return null;
}
};
The issue is when the url 'www.stackoverflow.com' is passed to the function, parseUrl returns http:localhost\www.stackoverflow.com
should I use to obtain the value http://www.stackoverflow.com?
You don't really need any external lib for this:
const parser = document.createElement('a');
parser.href = '//www.stackoverflow.com';
console.log(`${parser.protocol}//${parser.hostname}`); // 'https://stackoverflow.com'
I'm building a Facebook Page app in Classic ASP. I've been unable to match the signature that Facebook passes into the app as the first part of the POSTed signed_request.
Because there are few libraries for cryptography in VBScript, I'm using server side Javascript and the crypto-js library from https://code.google.com/archive/p/crypto-js/
I've tried to translate the PHP code example from Facebook's docs at https://developers.facebook.com/docs/games/gamesonfacebook/login#parsingsr into Javascript. I can generate an HMAC SHA256 hash of the signed_request payload but that doesn't match the signed_request signature.
I think the problem is that Facebook's signature is in a different format. It looks to be binary (~1抚Ö.....) while the HMAC SHA256 hash I'm generating is a hexadecimal string (7f7e8f5f.....). In Facebook's PHP example the hash_hmac function uses the raw binary parameter. So I think I need to either convert Facebook's signature to hexadecimal or my signature to binary in order to do an "apples-to-apples" comparison and get a match.
Here's my code:
/* Use the libraries from https://code.google.com/archive/p/crypto-js/
crypto-js/crypto-js.min.js
crypto-js/hmac-sha256.min.js
crypto-js/enc-base64.min.js
*/
var signedRequest = Request.queryString("signed_request")
var FB_APP_SECRET = "459f038.....";
var arSR = signedRequest.split(".");
var encodedSig = arSR[0];
var encodedPayload = arSR[1];
var payload = base64UrlDecode(encodedPayload);
var sig = base64UrlDecode(encodedSig);
var expectedSig;
expectedSig = CryptoJS.HmacSHA256(encodedPayload, FB_APP_SECRET); // Unaltered payload string; no match
expectedSig = CryptoJS.HmacSHA256(payload, FB_APP_SECRET); // base64-decoded payload string; no match
if (sig == expectedSig) {
Response.write(payload);
} else {
Response.write("Bad signature");
}
function base64UrlDecode(input) {
// Replace characters and convert from base64.
return Base64.decode(input.replace("-", "+").replace("_", "/"));
}
After looking into the crypto-js documentation about encoding I found the solution. The de-/encoding methods provided by crypto-js are listed under 'Encoders' at the bottom of https://code.google.com/archive/p/crypto-js/ (Thanks for the nudge, CBroe.)
The solution was to use .toString() on the signatures. It seems like crypto-js uses a word format that was preventing a comparison match. I did also switch to using the base64 decoding provided by crypto-js in order to stick with one library.
Here's my updated code:
/* Use the libraries from https://code.google.com/archive/p/crypto-js/
crypto-js/crypto-js.min.js
crypto-js/hmac-sha256.min.js
crypto-js/enc-base64.min.js
*/
var signedRequest = Request.queryString("signed_request")
var FB_APP_SECRET = "459f038.....";
var arSR = signedRequest.split(".");
var encodedSig = arSR[0];
var encodedPayload = arSR[1];
var payload = base64UrlDecode(encodedPayload);
var sig = base64UrlDecode(encodedSig);
var expectedSig = CryptoJS.HmacSHA256(encodedPayload, FB_APP_SECRET); /******** Correct payload */
if (sig.toString() != expectedSig.toString()) { /******* Use .toString() to convert to normal strings */
Response.write(payload);
} else {
Response.write("Bad signature");
}
function base64UrlDecode(input) {
return CryptoJS.enc.Base64.parse( /******** Decode */
input.replace("-", "+").replace("_", "/") // Replace characters
);
}
I recently implemented this for their required user data deletion webhook. No external dependencies needed anymore:
const crypto = require('crypto');
function parseSignedRequest(signedRequest, secret) {
const [signatureReceived, encodedPayload] = signedRequest.split('.', 2);
const payload = b64decode(encodedPayload)
const data = JSON.parse(payload);
const hmac = crypto.createHmac('sha256', secret).update(payload);
const expectedSignature = hmac.digest('base64');
if (signatureReceived === expectedSignature) {
return data;
} else {
throw new Error("Signature mismatch");
}
}
function b64decode(data) {
const buff = Buffer.from(data, 'base64');
return buff.toString('ascii');
}
It's a translation of their example PHP code. I also have a repo setup with tests.
I found this worked for me.
const crypto = require('crypto')
const _atob = (str) => Buffer.from(str, 'base64').toString('binary')
const parseSignedRequest = (signed_request, app_secret) => {
const [encoded_sig, payload] = signed_request.split('.')
const json = _atob(payload)
const data = JSON.parse(json)
if (!data.algorithm || data.algorithm.toUpperCase() !== 'HMAC-SHA256') {
return {error: true, type: 'Unknown algorithm. Expected HMAC-SHA256'}
}
// check sig
const expected_sig = crypto.createHmac('sha256', config.facebook.app_secret)
.update(payload).digest('base64')
.replace(/\+/g, '-').replace(/\//g, '_')
.replace(/=/g, '')
if (encoded_sig !== expected_sig) {
return ({error: true, type: 'invalid_signature'})
}
return {error: false, parsedRequest: data}
}
const {error, type, parsedRequest} = parseSignedRequest(signed_request)
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')
I want to validate a URL of the types:
www.google.com
http://www.google.com
google.com
using a single regular expression, is it achievable? If so, kindly share a solution in JavaScript.
Please note I only expect the underlying protocols to be HTTP or HTTPS. Moreover, the main question on hand is how can we map all these three patterns using one single regex expression in JavaScript? It doesn't have to check whether the page is active or not. If the value entered by the user matches any of the above listed three cases, it should return true on the other hand if it doesn't it should return false.
There is no need to use a third party library.
To check if a string is a valid URL
const URL = require("url").URL;
const stringIsAValidUrl = (s) => {
try {
new URL(s);
return true;
} catch (err) {
return false;
}
};
stringIsAValidUrl("https://www.example.com:777/a/b?c=d&e=f#g"); //true
stringIsAValidUrl("invalid"): //false
Edit
If you need to restrict the protocol to a range of protocols you can do something like this
const { URL, parse } = require('url');
const stringIsAValidUrl = (s, protocols) => {
try {
new URL(s);
const parsed = parse(s);
return protocols
? parsed.protocol
? protocols.map(x => `${x.toLowerCase()}:`).includes(parsed.protocol)
: false
: true;
} catch (err) {
return false;
}
};
stringIsAValidUrl('abc://www.example.com:777/a/b?c=d&e=f#g', ['http', 'https']); // false
stringIsAValidUrl('abc://www.example.com:777/a/b?c=d&e=f#g'); // true
Edit
Due to parse depreciation the code is simplified a little bit more. To address protocol only test returns true issue, I have to say this utility function is a template. You can adopt it to your use case easily. The above mentioned issue is covered by a simple test of url.host !== ""
const { URL } = require('url');
const stringIsAValidUrl = (s, protocols) => {
try {
url = new URL(s);
return protocols
? url.protocol
? protocols.map(x => `${x.toLowerCase()}:`).includes(url.protocol)
: false
: true;
} catch (err) {
return false;
}
};
There's a package called valid-url
var validUrl = require('valid-url');
var url = "http://bla.com"
if (validUrl.isUri(url)){
console.log('Looks like an URI');
}
else {
console.log('Not a URI');
}
Installation:
npm install valid-url --save
If you want a simple REGEX - check this out
The "valid-url" npm package did not work for me. It returned valid, for an invalid url. What worked for me was "url-exists"
const urlExists = require("url-exists");
urlExists(myurl, function(err, exists) {
if (exists) {
res.send('Good URL');
} else {
res.send('Bad URL');
}
});
Using the url module seems to do the trick.
Node.js v15.8.0 Documentation - url module
const url = require('url');
try {
const myURL = new URL(imageUrl);
} catch (error) {
console.log(`${Date().toString()}: ${error.input} is not a valid url`);
return res.status(400).send(`${error.input} is not a valid url`);
}
Other easy way is use Node.JS DNS module.
The DNS module provides a way of performing name resolutions, and with it you can verify if the url is valid or not.
const dns = require('dns');
const url = require('url');
const lookupUrl = "https://stackoverflow.com";
const parsedLookupUrl = url.parse(lookupUrl);
dns.lookup(parsedLookupUrl.protocol ? parsedLookupUrl.host
: parsedLookupUrl.path, (error,address,family)=>{
console.log(error || !address ? lookupUrl + ' is an invalid url!'
: lookupUrl + ' is a valid url: ' + ' at ' + address);
}
);
That way you can check if the url is valid and if it exists
I am currently having the same problem, and Pouya's answer will do the job just fine. The only reason I won't be using it is because I am already using the NPM package validate.js and it can handle URLs.
As you can see from the document, the URL validator the regular expression based on this gist so you can use it without uing the whole package.
I am not a big fan of Regular Expressions, but if you are looking for one, it is better to go with a RegEx used in popular packages.