В этот раз мы узнаем, как работать Download Manager API, которое появилось в новой версии Mojo SDK v1.2. В этой статье рассматривается возможность загрузки файлов с устройства на сервер с помощью Download Manager API.

Допустим, у нас есть Web-сервис, позволяющий загружать файлы с описанием (напримепр, фотографии). И вот, наше приложение должно обеспечить работу с этим сервисом для устройств, работающих под управлением WebOS.

Вот у нас есть простенькая HTML-форма для загрузки файлов:

upload.html

<form enctype="multipart/form-data" action="uploadtest.php" method="POST">
  <table border="0" cellspacing="5">
    <tr>
      <td>Info:</td>
      <td><input type="text" name="info" /></td>
    </tr>
    <tr>
      <td>File:</td>
      <td><input type="file" name="filename" /></td>
    </tr>
    <tr>
      <td colspan="2" align="center"><input type="submit" /></td>
    </tr>
  </table>
</form>

Также есть простенький Web-сервис, который принимает файлы и выполняет проверку по размеру, а также наличие описания. Выдачу результатов загрузки пользователь получает в HTML-виде:

uploadtest.php

<?php
do
{
    $max_file_size = 1024 * 100;
    $info = $_POST['info'];
    if(!$info) break;
    echo "Info - OK<br>";
    $filename = $_FILES['filename']['tmp_name'];
    $uploaded_size = filesize($filename);
    echo "File size: $uploaded_size<br>";
    if($uploaded_size == 0) break;
    echo "File size > 0<br>";
    if($uploaded_size > $max_file_size) break;
    echo "File size < MAX<br>";
    echo "PROFIT !!! : )";
    exit();
}
while(false);
echo "Error";
?>

В случае успешной загрузки файла на сервер мы получим в выдаче что-то подобное:

Info – OK
File size: 5092
File size > 0
File size < MAX
PROFIT !!! : )

И вот, мы сейчас узнаем как аналогичный функционал получить в приложении для Palm WebOS.

Создадим новое приложение и в нем сцену main.

app/views/main/main-scene.html

<div class="palm-group">
  <div class="palm-group-title">File info</div>
  <div class="palm-row first">
    <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
      <div class="title">
      	<div class="label">File</div>
        <div id="FileTextBox" x-mojo-element="TextField"></div>
      </div>
    </div>
  </div>
  <div class="palm-row last">
    <div class="palm-row-wrapper textfield-group" x-mojo-focus-highlight="true">
      <div class="title">
        <div class="label">Info</div>
        <div id="InfoTextBox" x-mojo-element="TextField"></div>
      </div>
    </div>
  </div>
</div>
<div id="SelectFileButton" x-mojo-element="Button"></div>
<div id="UploadButton" x-mojo-element="Button"></div>
<div id="ExitButton" x-mojo-element="Button"></div>
<div id="info" class="palm-body-text"></div>

Сцена содержит группу с двумя текстовыми полями. В первом поле можно ввести имя файла, который будет загружен на сервер, а во втором – текстовое описание. Также на сцене находятся три кнопки: выбор файла, загрузка, выход.

app/assistants/main-assistant.js

function MainAssistant() {
}

MainAssistant.prototype.setup = function() {
    this.controller.setupWidget("FileTextBox",
        {
            modelProperty: 'value'
        },
        this.fileModel =
        {
            value: ''
        });
    this.controller.setupWidget("InfoTextBox",
        {
            modelProperty: 'value',
            autoFocus: true
        },
        this.infoModel =
        {
            value: ''
        });
    this.controller.setupWidget("SelectFileButton",
        {}, this.selectFileModel = {label: "Select File"});
    this.controller.setupWidget("UploadButton",
        {
            type: Mojo.Widget.activityButton
        },
        this.uploadButtonModel =
        {
            label: "Upload",
            disabled: true
        });
    this.controller.setupWidget("ExitButton", {}, {label: "Exit"});

    Mojo.Event.listen($(SelectFileButton), Mojo.Event.tap, this.onSelectFile.bind(this));
    Mojo.Event.listen($(UploadButton), Mojo.Event.tap, this.onUpload.bind(this));
    Mojo.Event.listen($(ExitButton), Mojo.Event.tap, this.onExit.bind(this));
    Mojo.Event.listen($(FileTextBox), Mojo.Event.propertyChange,
        this.onPropertyChanged.bind(this));
    Mojo.Event.listen($(InfoTextBox), Mojo.Event.propertyChange,
        this.onPropertyChanged.bind(this));
}

MainAssistant.prototype.onPropertyChanged = function(value) {
}

MainAssistant.prototype.onSelectFile = function() {
}

MainAssistant.prototype.onUpload = function() {
}

MainAssistant.prototype.onExit = function() {
    this.controller.stageController.getAppController().closeAllStages();
    window.close();
}

MainAssistant.prototype.cleanup = function(event) {
    Mojo.Event.stopListening($(SelectFileButton), Mojo.Event.tap, this.onSelectFile);
    Mojo.Event.stopListening($(UploadButton), Mojo.Event.tap, this.onUpload);
    Mojo.Event.stopListening($(ExitButton), Mojo.Event.tap, this.onExit);
}

Кнопка Upload имеет тип Activity Button – при нажатии на такой кнопке появляется индикатор активности.

К текстовым полям мы подключили обработчик onPropertyChanged(), который будет вызываться при вводе текста с клавиатуры.

В методе cleanup() мы отключаем все обработчики событий от виджетов. Это необходимо делать всегда для того чтобы не ьыло утечек памяти.

В результате у нас должно получиться такое:

WebOS Uploader GUI

Теперь можно переходить к реализации логики работы приложения.

Во-первых, надо обеспечить проверку ввода данных пользователем. Мы не можем запускать загрузку до того, как пользователь введет имя файла и описание в текстовые поля. Для проверки введенных значений у нас есть метод onPropertyChanged().

MainAssistant.prototype.onPropertyChanged = function(value) {
    do
    {
        if(this.fileModel.value.length == 0) break;
        if(this.infoModel.value.length == 0) break;
        if(this.uploadButtonModel.disabled)
        {
            this.uploadButtonModel.disabled = false;
            this.controller.modelChanged(this.uploadButtonModel);
        }
        return;
    }
    while(false);
    if(!this.uploadButtonModel.disabled)
    {
        this.uploadButtonModel.disabled = true;
        this.controller.modelChanged(this.uploadButtonModel);
    }
}

Теперь надо дать возможность пользователю выбрать файл для загрузки. Сделать это можно с помощью виджета FilePicker.

MainAssistant.prototype.onSelectFile = function() {
    Mojo.FilePicker.pickFile(
        {
            onSelect: this.onFileSelected.bind(this)
        },
        this.controller.stageController);
}

MainAssistant.prototype.onFileSelected = function(result) {
this.fileModel.value = result.fullPath;
this.controller.modelChanged(this.fileModel);
this.onPropertyChanged();
}

Webos FilePicker

К сожалению, после того, как мы изменили значение в модели и вызвали modelChanged(), метод onPropertyChanged() автоматически не вызывается, поэтому нам приходится вызывать его руками.

Теперь надо реализовать логику отправки файла:

MainAssistant.prototype.onUpload = function() {
    try
    {
        var posturl = "http://192.168.0.1/uploadtest.php";
        this.controller.serviceRequest('palm://com.palm.downloadmanager/',
            {
                method: 'upload',
                parameters:
                {
                    fileName: this.fileModel.value,
                    fileLabel: 'filename',
                    url: posturl,
                    contentType: 'application/x-www-form-urlencoded',
                    subscribe: true,
                    postParameters:
                    [
                        {key: 'info' , data: this.infoModel.value}
                    ]
                },
                onSuccess : this.onUploadStatus.bind(this),
                onFailure: function(e)
                {
                    this.uploadTicket = 0;
                    this.enableControls(true);
                    this.uploadButtonModel.disabled = false;
                    this.controller.modelChanged(this.uploadButtonModel);
                    $(UploadButton).mojo.deactivate();
                    Mojo.Controller.errorDialog("Upload failed, " + e.errorCode + " : " + e.errorText);
                }
            });
    }
    catch(e)
    {
        Mojo.Controller.errorDialog(e);
    }
}

Для отправки файла на сервер мы испотльзуем метод upload сервиса Download Manager. Параметры у этого метода такие:

  • fileName – имя файла на устройстве
  • fileLabel – это значение соответствует атрибута name тега input в HTML-форме.
  • url – адрес скрипта, которому будет отправлен запрос
  • subscribe – если указать значение этого параметра равным true, то мы выполним подписку на уведомления о состоянии загрузки. Это мы и сделали
  • onSuccess – функция, которая будет вызываться при получении уведомления о состоянии загрузки
  • onFailure – функция, которая будет вызвана в случае ошибки загрузки
  • postParameters – массив, содержащий POST-параметры запроса. Каждый элемент этого массива должен содержать поле key с именем параметра и поле data со значением параметра. В этом массиве мы передаем параметр info (аналогичный input с типом text есть в HTML-форме).
MainAssistant.prototype.enableControls = function(value) {
this.fileModel.disabled = !value;
this.infoModel.disabled = !value;
this.selectFileModel.disabled = !value;
this.controller.modelChanged(this.fileModel);
this.controller.modelChanged(this.infoModel);
this.controller.modelChanged(this.selectFileModel);
}

MainAssistant.prototype.onUploadStatus = function(response) {
    if(response.subscribed)
    {
        this.enableControls(false);
        this.uploadButtonModel.disabled = true;
        this.controller.modelChanged(this.uploadButtonModel);
        $(UploadButton).mojo.activate();
        this.uploadTicket = response.ticket;
    }
    else if(response.completed)
    {
        this.uploadTicket = 0;
        this.enableControls(true);
        this.uploadButtonModel.disabled = false;
        this.controller.modelChanged(this.uploadButtonModel);
        $(UploadButton).mojo.deactivate();

        $(info).update(response.responseString);
    }
}

Как видно из кода, в функции, которая отрабатывает при получении состояния загрузки, мы проверяем значение поля subscribe объекта-параметра, который содержит всю информацию о загрузке. Поле subscribe содержит значение true только когда загрузка успечно началась. Если мы получаем true в поле subscribe, то деактивируем все текстовые поля, а также кнопку Select File и кнопку Upload. Также  мы отображаем индикатор на кнопке Upload с помощью метода activate().

Когда мы получаем значение true в поле completed, это значит что загрузка завершилась. Мы активируем виджеты и убираем индикатор с кнопки Upload с помощью метода deactivate(). Затем мы отображаем ответ от сервера на сцене.

Кроме всего прочего нам необходимо обработать ситуацию, когда приложение завершается до того, как файл загружен на сервер. После начала загрузки мы можем получить уникальный идентификатор загрузки с помощью поля ticket объекта, возвращаемого сервисом Download Manager. В методе cleanup() сцены нам необходимо проверить это значение, если оно ненулевое, то завершить загрузку принудительно.

MainAssistant.prototype.cleanup = function(event) {
        Mojo.Event.stopListening($(SelectFileButton), Mojo.Event.tap, this.onSelectFile);
        Mojo.Event.stopListening($(UploadButton), Mojo.Event.tap, this.onUpload);
        Mojo.Event.stopListening($(ExitButton), Mojo.Event.tap, this.onExit);
        if(this.uploadTicket != 0)
        {
                var handler = function(res) { this.uploadTicket = 0 }
                this.controller.serviceRequest('palm://com.palm.downloadmanager/',
                {
                    method: 'cancelDownload',
                    parameters:
                    {
                        ticket : this.uploadTicket
                    },
                    onSuccess : handler.bind(this),
                    onFailure : handler.bind(this)
                });
    }
}

WebOS Uploader
Скачать исходный код примера.

Previous ArticleNext Article
Технический директор IT-Dimension, компании-разработчика кросс-платформенного программного обеспечения

Leave a Reply

Your email address will not be published. Required fields are marked *

В.

Вышел Palm Mojo SDK 1.3.1

Вышла новая версия средств разработки приложений для Palm WebOS – Mojo SDK 1.3.1.
В этой версии:

  • Улучшена поддержка Palm Pixi (у него экран по размерам меньше чем в Palm Pre).
  • Появилось API для предотвращения затемнения экрана при простое.
  • Добавлена автогенерирование сцены Help/Support
  • Сделано много изменений и дополнений в документации.

С полным списком изменений можно ознакомиться здесь.

Скачать Palm Mojo SDK 1.3.1

А.

Анонсирована первая книга по программированию для Palm webOS

Palm webOS BookВ блоге разработчиков Palm webOS появился анонс первой книги по программированию для этой ОС.
Называется книга “Palm webOS: Developing Applications in JavaScript Using the Palm Mojo Framework”.

Первая глава книги доступна для загрузки в формате PDF на сайте разработчиков Palm webOS.

Еще одна хорошая новость: 25го февраля в 22.00 (10 a.m. PT) состоится вебкаст с обзором средств разработки для Palm webOS.

Лень искать?

Скачать первую главу книги прямо здесь.