Эта топик результат выполнения тестового задания, со сроком выполнения 1 неделя. Поэтому код на уровне прототипа.
Amazon Simple Storage Service(S3)
С моей колокольни(пользователя REST API) S3-это хранилище типа key-value, при этом допускается создавать ключи с пустым значением. При таком подъходе струткура хранилища плоская, никакого привычного нам дерева каталогов и файлов, которое мы привыкли видеть в файловой системе.
Как водится среди key-value хранилишь, если нужно что-то отличное от хеш-таблицы,
до говариваемся о представление ключа, а все остальные хотелки выночим на уровень приложения.
В данном случае будут исользоватся следующие соглашения:
- foo/bar/-это каталог foo, c подкаталогом bar. Используется для создания пустых каталогов;
- foo/bar-это каталог foo, с файлом bar;
- на этом наборе данных в качестве разделителя местами использовался символ ‘:’.
В зависимости от того какое приложение вы создаете, то выбираете удобное соглашение формирования ключей.
Аутентификация в S3
Здесь нет никаких токенов. Тут нужно подписвать каждый запрос. В Signing AWS Requests By Using Signature Version 4 расписано по шагам и есть примеры для разных языков программирования. Тут отмечу, что там используется HMAC-SHA256, для его вычисления можно использовать класс QMessageAuthenticationCode появившийся в Qt5.1.
Запросы
В каждом запросе кроме заголовка Authorization передаю еще следующие: Date, Host, x-amz-date, x-amz-content-sha256. x-amz-content-sha256 — это хеш тела запроса подсчитанный методом SHA256. Этот хеш так же добавляется в «канонический запрос» при вычислении сигнатуры запроса. Да же если идет GET запрос у которого отсутствует тело, этот заголово все равно передается. Вычисляется стандартным классом QCryptographicHash.
Далее идет таблица используемых запросов
GET / HTTP/1.1 Host s3.amazon.com |
Получить список бакетов |
GET / HTTP/1.1 Host BucketName.s3.amazon.com |
Получить список объектов в бакете BucketName |
GET /ObjectName HTTP/1.1 Host BucketName.s3.amazon.com |
Скачать файл из бакета BucketName. Целиком или часть. |
PUT /ObjectName HTTP/1.1 Host BucketName.s3.amazon.com |
Загрузить файл на S3, в бакет BucketName |
Небольшой комментарий по второму запросу, получающему список объектов. Ключи в ответе упорядочены в лексикографическом порядке. По умолчанию запрос возвращает первую 1000 ключей. Если запрос обрезается,
то выставляется флаг IsTruncated:true, в этом случае для получения следующей партии ключей передается marker указывающий на последний полученный ключ. Значение 1000 можно изменить параметром max-keys.
Строим дерево
По хорошему, для уменьшения потребления памяти, дерево хорошо бы хранить на диске. Но т.к. времени было в обрез, сделал традиционно в памяти:
struct ObjectTree { TreeNode* root; ASSS::ObjectInfoList objects; }; struct TreeNode { typedef QList<TreeNode*> NodeList; enum Type { Undef, Leaf, Node }; Type type; TreeNode* parent; NodeList children; int objectPos; QString segment; bool isRoot; };
В узлах дерева хранятся сегменты пути(в UI они отображаются каталогами), в листьях позиция объекта в списке objects. Алгоритм вставки простой: разбиваем ключ на сегменты, идем по дереву и вставляем новые узлы.
Примечательно что из-за того, что ключи упорядочены, а вставка не нарушает порядок, получается дерево поиска.
Немного «Model/View Programming»
Модель дерева
Когда есть готовая древовидная структура легко пишется враппер под интерфейс QAbstractItemModel. Все валидные модельные индексы, которые подаются в методы класса QAbstractItemModel будут создаваться методом:
QModelIndex QAbstractItemModel::createIndex(int row, int column, void * ptr = 0) const
Третьим параметром передается указатель на элемент дерева TreeNode.
В дальнейшем этот указатель будет возвращаться методом индекса:
void * QModelIndex::internalPointer() const
Далее прокомментирую какой смысл имеют методы QAbstractItemModel при разработке дерева.
virtual QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
В моем случае существенными параметрами является row и parent. Данный метод создает модельный индекс для потомка узла parent, который стоит на позиции row.
В моем случае это позиция узла в списке TreeNode.children.
virtual int rowCount(const QModelIndex & parent = QModelIndex()) const;
Количество потомков у узла parent. Внутри себя вызывает TreeNode.children.size()
virtual QModelIndex parent(const QModelIndex & index) const;
Получить модельный индекс родительского узла. Может потребовать до двух переходов по TreeNode.parent, что бы определить правильную позицию узла и передать её в createIndex
Правильно реализовав эти три метода, у вас будет дерево.
Делегат
На листьях моего дерева метаданные возвращаемые AWS, то среди них нет «имени для отображения».
Значит это имя надо извлечь из ключа. Из соображений, что в ListView и TaleView удобно отображать ключи, а в TreeView отображаемых елементов меньше чем в TableView, было принято решение для TreeView сделать делегат. Такое решение вполне соответствует Model/View Programming. Например если строка слишком большая и не вмещается в отображаемую область,
то делегат может обрезать её и дописать многоточие.
Отнаследовавшись от QItemDelegate переопределил:
void drawDisplay(QPainter * painter, const QStyleOptionViewItem & option, const QRect & rect, const QString & text) const
Результат:
Недочеты в юзабилити
О юзабилити вообще не думал, как следствие приходится вручную определять регион, хотя его можно вытащить из хоста.
А так же неочевидность назначения кнопки на тулбаре.
Что в остатке?
Есть код с примерами использования который можно задействовать,
если вам чем то не устраивает Qt Cloud Services.
ссылка на оригинал статьи http://habrahabr.ru/post/264715/
Добавить комментарий