Tutorial Adobe AIR: aplikacja do backupu plików na serwer.

Adobe AIR

Poziom zaawansowany

Swego czasu postanowiłem napisać prostą aplikację to szybkiego tworzenia kopii zapasowych plików, i umieszczania ich na moim serwerze. Program ten stworzyłem w Adobe AIR plus zaplecze w PHP. W tym tutorialu opiszę proces powstawania tej aplikacji.

Tutorial Adobe AIR – tworzenie aplikacji

Przeczytamy tutaj m.in. o Drag&Drop, komunikacji z serwerem i obsłudze plików w Adobe AIR.

Kod źródłowy jako projekt Aptany oraz plik wynikowy .air do pobrania tutaj.

Podstawowe wytyczne dla aplikacji

Projekt przyjął następujące wytyczne:

– aplikacja do tworzenia kopii zapasowych plików,

– ładne, niewielkie okno, które możemy przeciągać,

– pliki wybieramy metodą drag&drop,

– jednorazowo możemy wysłać określoną liczbę plików,

– przycisk ‚upload’, po kliknięciu wysyła wybrane pliki na serwer,

– po stronie serwera pliki odbierane są przez skrypt PHP, który automatycznie zarządza nazwami,

– po wysłaniu plików program wyczyści i przygotuje bufory na nowy pakiet plików.

Zaczynamy tworzenie projektu Adobe AIR

Tworzymy nowy projekt Adobe AIR w Aptanie. Istotne są tutaj ustawienia okna, ponieważ chcemy sami wystylizować je w CSS. Dlatego w Window Options ustawiamy:

– w Window Style wybieramy Custom Chrome (transparent)

– szerokości i wysokości okna na 550 x 350

W sekcji Select Ajax Libraries natomiast, wybieramy bibliotekę jQuery.

Dodajemy do projektu następujące pliki:

– css/style.css

– icons/ajax_s.gif

– lib/myapp.js

– processor.php

Struktura projektu wygląda teraz następująco:

Tutorial Adobe AIR - struktura projektu

Tutorial Adobe AIR – struktura projektu

Otwieramy plik AirBackup.html i w sekcji head dodajemy pliki:

– lib/myapp.js

– css/style.css

Powinniśmy dodać także znacznik meta określający kodowanie, takie samo jak mamy ustawione w edytorze Aptany. Ja sam zawsze i wszędzie używam utf-8, zwłaszcza że często pracuję nad aplikacjami, które są dostępne w wielu językach, a kodowanie utf-8 zwyczajnie upraszcza sprawę.

Następnie w sekcji body tworzymy interfejs naszej aplikacji:

<body>
  <div id="my-window">
    <div id="top-bar">
      <span id="app-label">
        AirBackup 1.1 by javascript-html5-tutorial.pl
      </span>
      <a href="#" id="app-close">X</a>
    </div>

    <div id="drop-area" 
      ondragenter="handleDragEnter(event);" 
      ondragover="handleDragOver(event);" 
      ondrop="handleDrop(event);"> 
        Proszę przeciągnąć pliki na ten obszar 
    </div>

    <div id="files">
      <ul></ul>
    </div>

    <span onclick="startUpload()" id="control-area">
      <img src="icons/AIRApp_32.png" 
          style="vertical-align: middle;" />
      Wyślij
    </span>

    <div id="progress_pointer" style="display: none;">
      Przetwarzanie...<img src="icons/ajax_s.gif" />
    </div>

    <div class="clear"></div>
  </div>
</body>

Element o id=”top-bar” stanowi pasek tytułowy okna, drop-area to obszar, na który będziemy upuszczać pliki, files będzie listą plików, mamy też element uruchamiania wysyłania oraz ukryty tzw. indicator, spotykany często w aplikacjach korzystających z AJAX.

Zwróćmy uwagę na zdarzenia z drop-area:


ondragenter="handleDragEnter(event);"
ondragover="handleDragOver(event);"
ondrop="handleDrop(event);"

Przypisane są do nich funkcje obsługi. Ich definicje znajdą się w pliku lib/myapp.js. Prostota implementacji Drag&Drop w Adobe AIR jest wręcz niewiarygodna, o czym za chwilę się przekonamy.

Aby nasze okno poprawnie się wyświetlało i było przyjazne dla użytkownika, potrzebujemy zdefiniować style CSS. Ja przygotowałem definicje jak poniżej.


Plik css/style.css:

/*
 * Definicje wyglądu aplikacji AirBackup 1.0
 * (c) javascript-html5-tutorial.pl
 */

.clear {
    clear: both;
}

#my-window {
    width: 540px;
    height: 340px;
    background-color: #fff;
    border: 1px outset #00d;
    overflow: hidden;
    -webkit-border-radius: 10px;
}

#app-label {
    float: left;
    padding: 4px;
    cursor: move;
}

#top-bar {
    height: 30px;
    border-bottom: 1px solid #00a;
    text-align: right;
    padding: 2px 8px 0 4px;
    cursor: move;
}

#top-bar a {
    font-weight: bold;
    color: #f00;
    text-decoration: none;
    line-height: 30px;
}

#files {
    list-style-type: square;
    float: left;
    width: 300px;
    height: 200px;
    overflow: auto;
    padding: 5px;
    font-size: 9px;
}

#drop-area {
    float: left;
    width: 500px;
    height: 90px;
    border: 1px solid #00d;
    background-color: #efe;
    margin: 10px;
    padding-top: 10px;
    padding-left: 10px;
    font-size: 8pt;
}

#control-area {
    cursor: pointer;
    float: right;
    padding: 10px
}

#info {
    padding: 8px 0 4px 8px;
    font-size: 9pt;
}

Przejdźmy do sedna – plik myapp.js

Jest to skrypt JavaScript, z implementacją obsługi funkcji aplikacji.

Sekcja konfiguracyjna:

// adres docelowy
const SERVER_URL = "http://www.directcode.eu/airbackup/processor.php";

// maksymalna ilość plików, jaką możemy wysłać jednorazowo
const FILES_MAX_AMOUNT = 10;

Bardzo ważne jest, aby w stałej SERVER_URL ustawić poprawną ścieżkę do swojego skryptu na serwerze. Skrypt ten (przedstawię go za chwilę) odbiera i zapisuje wysłane przez nas pliki.

Realny skrypt podany w moich przykładach, to jest http://www.directcode.eu/airbackup/processor.php, ze względów bezpieczeństwa ma usunięty kod odpowiedzialny za zapis plików na serwerze.

Dalej dodajemy trochę kodu bazującego na jQuery, obsługującego nasze okno:

$(document).ready(function() {
    // przeciąganie okna chwytając za element top-bar
    $("#top-bar").mousedown(function() {
        window.nativeWindow.startMove();
    });
    
    // zamykanie aplikacji
    $("#app-close").click(function() {
        window.nativeWindow.close();
        return false;
    });
});

Następnie definiujemy tablice globalne:

// nazwy plików
var backupFileNames = new Array();

// tablica obiektów typu File
var backupFiles = new Array();

oraz funkcje obsługi zdarzeń

function handleDragEnter(event) {
    event.preventDefault();
}

function handleDragOver(event) {
    event.preventDefault();
}

function handleDrop(event) {
    
    // kontrola ilości plików do jednorazowego wysłania
    if (backupFiles.length >= FILES_MAX_AMOUNT) {
        alert('Wyślij bieżące pliki. Limit 1 pakietu wynosi '
            + FILES_MAX_AMOUNT);
        
        return;
    }
    
    // budujemy listę plików
    var fileList = event.dataTransfer.getData(air.
        ClipboardFormats.FILE_LIST_FORMAT);
    
    for (var i in fileList) {
        if ($.inArray(fileList[i].name, backupFileNames) == -1) 
        {
            backupFileNames.push(fileList[i].name);
            backupFiles.push(fileList[i]);
            $("#files ul").append('<li>' + fileList[i].name + '</li>');
        }
    }
    
    showFilesInfo();
}

Implementujemy funkcje pomocnicze:

// wyświetlanie informacji o plikach
function showFilesInfo() {
    if ($("#info").length == 0) {
        $("#top-bar").after('<div id="info"></div>');
    }
    else {
        $("#info").empty();
    }
    
    var _size = getTotalFileSize() / 1024; // kB
    
    // formatuj do 2 miejsc po przecinku
    var _formatted_size = (Math.floor(_size * 100)) / 100;
    
    $("#info")
        .append("Liczba plików: " + backupFiles.length + "<br />")
        .append("Sumaryczny rozmiar plików: " 
            + _formatted_size + " kB");
}

// obliczanie rozmiaru dodanych plików
function getTotalFileSize() {
    var sum = 0;
    
    for (var i in backupFiles) {
        sum += backupFiles[i].size;
    }
    
    return sum;
}

// czyszczenie
function doCleanups() {
    $("#progress_pointer").hide();
    
    backupFileNames = [];
    backupFiles = [];
    
    $("#files").empty();
    $("#info").empty();
}

I gwóźdź programu:

// funkcja wykonująca upload plików na serwer
function startUpload() {
    
    if (backupFiles.length == 0) {
        alert('Brak plików do wysłania na serwer.');
        return;
    }
    
    var boundary = '--AaB03x';
    var request = null;
    var file = null;
    
    // nowe żądanie
    request = new air.URLRequest(SERVER_URL);
    
    request.useCache = false;
    request.contentType = 'multipart/form-data, boundary=' 
        + boundary;
    request.method = 'POST';
    
    var buffer = new air.ByteArray();
    
    // pokaż indicator
    $("#progress_pointer").show();
    
    // przetwórz listę plików
    for (x in backupFiles) {
        
        file = new air.File(backupFiles[x].nativePath);
        
        fileStream = new air.FileStream();
        fileStream.open(file, air.FileMode.READ);
        fileContents = new air.ByteArray();
        fileStream.readBytes(fileContents, 0, file.size);
        fileStream.close();
        
        buffer.writeUTFBytes("--" + boundary + "\r\n");
        buffer.writeUTFBytes("content-disposition: form-data; 
             name=\"Filedata\"; filename=\"" + file.name 
        + "\"\r\n");

        buffer.writeUTFBytes("Content-Transfer-Encoding: binary\r\n");
        buffer.writeUTFBytes("Content-Length: " + file.size + "\r\n");
        buffer.writeUTFBytes("Content-Type: application/octet-stream\r\n");
        buffer.writeUTFBytes("\r\n");
        
        buffer.writeBytes(fileContents, 0, fileContents.length);
        buffer.writeUTFBytes("\r\n--" + boundary + "--\r\n");
        
        request.data = buffer;
        
        var loader = new air.URLLoader();
        
        loader.addEventListener(air.ProgressEvent.PROGRESS, 
        function(e) {
           // alert('progress: ' + e);
        });
        
        loader.addEventListener(air.IOErrorEvent.IO_ERROR, 
        function(e) {
            alert('error: ' + e.text);
        });
        
        loader.addEventListener(air.Event.COMPLETE, function(e) {
            if (loader.data) {
                alert(loader.data);
                doCleanups();
            }
        });
        
        loader.load(request);
    }
}

To wszystko. Możemy uruchomić i przetestować naszą aplikację.

Tutorial Adobe AIR - okno aplikacji

Tutorial Adobe AIR – okno aplikacji

Ja wybrałem do wysłania te same pliki dwukrotnie, więc na serwerze mam już zabezpieczone kopie moich plików:

Tutorial Adobe AIR - kopie plików na serwerze

Tutorial Adobe AIR – kopie plików na serwerze

A oto skrypt PHP działający po stronie serwera

Właściwie to najprostsza wersja skryptu. Zajmuje się obsługą przesłanych plików, a dodatkowo zawiera kod, który dodaje znacznik czasu do nazwy pliku, jeśli takowy już istnieje, aby go nie nadpisać:

    // plik processor.php

    // nalezy upewnic sie ze ten folder istnieje i skrypt 
    // ma prawa zapisu do niego
    $upload_dir = "./uploads/";
    
    $err_state = true;
    
    $temp_file = $_FILES['Filedata']['tmp_name'];
    $file_name = $_FILES['Filedata']['name'];
    $file_size = $_FILES['Filedata']['size'];
    
    // jesli istnieje dodaj UNIX Timestamp do nazwy jako prefix
    $tmp_file_name = "";
    if (file_exists($upload_dir . $file_name)) {
        $tmp_file_name = time() . '_' . $file_name;
        
        if (move_uploaded_file($temp_file, $upload_dir 
            . $tmp_file_name)) {
            $err_state = false;
            // ...
        }
    }
    else {
        if (move_uploaded_file($temp_file, $upload_dir 
        . $file_name)) {
            $err_state = false;
            // ...
        }
    }
    
    if (!$err_state) {
        // upload przebiegl pomyslnie
        echo "Sukces: przechwycono i zabezpieczono pliki.";
    } else {
        echo "Error: upload nie powiodl sie.";        
    }

Jeśli chcemy nadpisywać pliki, aby mieć na serwerze tylko najnowsze ich wersje, zmieniamy następujący kod:

$tmp_file_name = "";
if (file_exists($upload_dir . $file_name)) {
    $tmp_file_name = time() . '_' . $file_name;

    if (move_uploaded_file($temp_file, $upload_dir 
    . $tmp_file_name)) {
        $err_state = false;
        // ...
    }
}
else {
    if (move_uploaded_file($temp_file, $upload_dir 
    . $file_name)) {
        $err_state = false;
        // ...
    }
}

pisząc po prostu:

if (move_uploaded_file($temp_file, $upload_dir . $file_name)) {
    $err_state = false;
    // ...
}

I to wszystko – wytyczne projektu zostały spełnione.

Podsumowanie

Tak oto nauczyliśmy się kilku ciekawych zagadnień dotyczących Adobe AIR oraz mamy całkiem przydatną aplikację. Sam jej używam, gdyż wykonywanie kopii ważnych plików na serwer jest dla mnie bardzo istotne. A teraz mogę to zrobić jeszcze szybciej.

Mam nadzieję że Czytelnik jeszcze bardziej zainteresował się technologią Adobe AIR.

Źródła do pobrania tutaj.

Programista WWW i aplikacji mobilnych z wieloletnim doświadczeniem, początkujący bloger. Pasjonat programowania, nowych technologii, e-commerce, a także sportu i motoryzacji.

Twitter LinkedIn Google+ Skype Xing 

Podaj dalej: Share on Facebook0Tweet about this on TwitterShare on Google+2Share on LinkedIn1Share on Tumblr0Digg thisEmail this to someonePin on Pinterest1
Możesz skomentować leave a response, lub podać trackback z własnej strony.