Электрон 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;
}

0 ответов

Другие вопросы по тегам