Skip to content

Instantly share code, notes, and snippets.

@Alhadis
Created May 29, 2019 12:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alhadis/e58ed916df460fc213f54037dfbc75b1 to your computer and use it in GitHub Desktop.
Save Alhadis/e58ed916df460fc213f54037dfbc75b1 to your computer and use it in GitHub Desktop.
select-file.js
<!DOCTYPE html>
<html lang="en-AU">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="initial-scale=1, minimum-scale=1" />
<title>Cross-browser/Electron file selection</title>
</head>
<body>
<button value="1">Select one file, any type</button>
<button value="2">Select multiple files, any type</button>
<button value="3">Select one image</button>
<button value="4">Select multiple images</button>
<script src="select-file.js"></script>
<script>
"use strict";
document.body.onclick = e => {
const fn = results => console.log(results);
switch(+e.target.value){
case 1: selectFile(false).then(fn); break;
case 2: selectFile(true).then(fn); break;
case 3: selectFile(false, "jpg, jpeg, png, gif").then(fn); break;
case 4: selectFile(true, "jpg, jpeg, png, gif").then(fn); break;
}
};
</script>
</body>
</html>
"use strict";
const isNode =
!!("object" === typeof process
&& "object" === typeof global
&& "object" === typeof process.versions
&& "string" === typeof process.versions.node);
const isElectron = !!(isNode && process.versions.electron);
const readFile = async (path, raw = false) => {
// Node.js/Electron
if(isNode){
const {readFile} = require("fs");
return new Promise((resolve, reject) => {
readFile(path, raw ? null : "utf8", (error, data) =>
error ? reject(error) : resolve(data));
});
}
// Modern browser
else if("function" === typeof window.fetch)
return (await fetch(path))[raw ? "arrayBuffer" : "text"]();
// Ancient browser, probably running transpiled ES5 source
else return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", path);
xhr.responseType = raw ? "arraybuffer" : "text";
xhr.onreadystatechange = () => {
if(4 !== xhr.readyState) return;
xhr.status < 400
? resolve(raw && "string" === typeof xhr.response
? xhr.response.split("")
: xhr.response)
: reject(new Error(`HTTP 1.1/${xhr.status} ${xhr.statusText}`));
};
xhr.onerror = reject;
xhr.send();
});
};
async function selectFile(multiple = false, fileExts = null){
fileExts = fileExts && ("string" === typeof fileExts
? fileExts.trim().split(/\s+/)
: [...fileExts]).map(e => e.replace(/^\*?\.|,/g, ""));
// Electron
if(isElectron){
const {dialog, remote} = require("electron");
const options = {properties: ["openFile", "showHiddenFiles"]};
if(multiple) options.properties.push("multiSelections");
if(fileExts) options.filters = fileExts.map(e => ({extensions: [e]}));
return Promise.all(((dialog || remote.dialog).showOpenDialog(options) || [])
.map(path => readFile(path, true).then(data => [path, data])));
}
// Browsers
else{
if(!fileInput){
fileInput = document.createElement("input");
fileInput.type = "file";
}
fileInput.multiple = !!multiple;
fileInput.accept = fileExts ? fileExts.map(e => "." + e).join(",") : "";
const files = await new Promise(resolve => {
fileInput.onchange = () => resolve([...fileInput.files]);
fileInput.click();
});
fileInput.onchange = null;
fileInput.value = "";
return Promise.all(files.map(file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onabort = () => resolve();
reader.onerror = () => reject(reader.error);
reader.onload = () => resolve([file.name, reader.result]);
reader.readAsArrayBuffer(file);
})));
}
}
let fileInput = null;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment