Электрон 4 Windows -> электронный строитель -> автообновление: индивидуальное решение
Я создаю приложение для Windows, используя Electron. Для упаковки и распространения я использую электронный строитель. Electron-builder использует много пакетов, а для автообновлений использует Squirrel-windows.
Я боролся с автоматическим обновлением в Windows в течение 3 дней, и в конце я нашел рабочее решение, которое, кажется, не доставляет проблем.
Я не буду вдаваться в подробности того, что я попробовал и потерпел неудачу. Вместо этого я опубликую здесь решение, с которым я пришел.
Я делюсь этим с вами, ребята, чтобы увидеть, можете ли вы указать мне на любые недостатки, которые приведут к отказу моей системы, или, если это действительно серьезное решение, помочь тем, кто борется, как и я. По этой последней причине я публикую больше кода, чем это было бы необходимо, в надежде, что это поможет другим.
Логика такова:
- если подпапка
fullupdate
внутри пути текущего исполняемого файла не существует (см. позже, это будет уточнено), мы подключаемся к онлайн-серверу и проверяем, есть ли обновление, отправляя текущую версию приложения; - если нет обновления, ничего не делайте.
- если есть обновление, мы поручаем серверу вернуть
json
строка, содержащая URL-адрес, с которого мы можем скачать.exe
установщик производитсяelectron-builder
, NB: не.nupkg
(код сервера не указан:-)
). - мы загружаем файл и сохраняем его в подпапке
fullupdate
в локальной папке, в которой наше приложение в настоящее время сохраняется. Это должно быть "безопасно", так какelectron-builder
сохраняет приложение в текущей папке пользователяAppData
, поэтому у нас не должно быть проблем с разрешениями. - в конце загрузки мы создаем новый файл
update
внутри папкиfullupdate
чтобы убедиться, что загрузка успешно завершена. Мы могли бы также переименовать файл, но я предпочитаю этот путь. - в следующий раз приложение откроется:
- если папка
fullupdate
существует, мы проверяем, если файлupdate
существует. Если он не существует, загрузка не была завершена, поэтому мы удаляем папкуfullupdate
и снова вызвать удаленный сервер, чтобы начать все сначала. - иначе, если файл
update
существует, мы запускаем.exe
файл, который мы скачали, и верните true. Это предотвратит открытие приложением главного окна. Самое интересное, что программа обновления удалит всю старую версию приложения, сохраненную вAppData
(оставляя данные локального пользователя) и замените его новой версией. Таким образом мы избавимся и от папкиfullupdate
,
- если папка
Теперь код:
// we want to run this only on windows
var handleStartupEvent = function() {
if (process.platform !== 'win32') {
return false;
}
/////////////////
// MANUAL UPDATER
/////////////////
var appFolder = 'app-' + appVersion;
var pathApp = path.dirname(process.execPath);
var pathUpdate = pathApp + '\\fullupdate';
var checkupdateurl = 'https://api.mysite.com/getjson/' + appVersion.split('.').join('-');
function checkIfDownloaded(){
if (!fs.existsSync(pathUpdate)) checkUpdate();
else return checkIfInstallLocal();
}
function checkIfInstallLocal(){
if(fileExists('fullupdate\\update')) return installLocal();
else {
deleteFolderRecursive(pathUpdate);
checkUpdate();
}
}
function installLocal(){
cp.exec('fullupdate\\Update.exe', function( error, stdout, stderr){
if ( error != null ) {
console.log(stderr);
}
});
return true;
}
// from http://www.geedew.com/remove-a-directory-that-is-not-empty-in-nodejs/
var deleteFolderRecursive = function(path) {
if( fs.existsSync(path) ) {
fs.readdirSync(path).forEach(function(file,index){
var curPath = path + "/" + file;
if(fs.lstatSync(curPath).isDirectory()) deleteFolderRecursive(curPath);
else fs.unlinkSync(curPath);
});
fs.rmdirSync(path);
}
};
// from http://stackru.com/questions/4482686/check-synchronously-if-file-directory-exists-in-node-js
function fileExists(path) {
try {
return fs.statSync(path).isFile();
}
catch (e) {
if (e.code == 'ENOENT') { // no such file or directory. File really does not exist
return false;
}
throw e; // something else went wrong, we don't have rights, ...
}
}
function checkUpdate(){
https.get('https://api.mysite.com/getjson/' + app.getVersion().split('.').join('-'), (res) => {
res.setEncoding('utf8');
res.on('data', function(chunk) {
if(chunk) thereIsUpdate(chunk);
});
}).on('error', (e) => {
console.log(e);
});
}
function thereIsUpdate(chunk){
var data = JSON.parse(chunk);
if(data && data.url) getNewUpdate(data.urlsetup);
}
function getNewUpdate(url){
fs.mkdirSync(pathUpdate);
var file = fs.createWriteStream(pathUpdate + '/Update.exe');
var responseSent = false; // flag to make sure that response is sent only once.
var request = https.get(url, function(response) {
response.pipe(file);
file.on('finish', () =>{
file.close(() => {
if(responseSent) return;
responseSent = true;
});
fs.closeSync(fs.openSync(pathUpdate + '/update', 'w'));
});
});
}
if(checkIfDownloaded()) return true;
/////////////////////////
// SQUIRREL EVENTS HANDLER
//////////////////////////
// see http://stackru.com/questions/30105150/handle-squirrels-event-on-an-electron-app
};
// here we call the function. It is before the opening of the window, so that we prevent the opening if we are updating, or if there is a Squirrel event going on (see SO question, link above)
if (handleStartupEvent()) {
return;
}