null

Работа со StreamedData на backend

Содержание:

0. Что важно знать o StreamedContent
1. Скачивание файлов через REST 
2. Проверка расширения файла
3. Сохранение в базу и скачивание из базы
4. Отображение картинки в jsf
5. Добавление кнопки для скачивания файла
6. Добавление кнопки для загрузки данных

 

Это общая статья с различными примерами использования StreamedContent’a: как скачивать, как загружать и как пользоваться 

В этой части статьи я расскажу о работе со treamedContent’ом на стороне бэкенда. В этой статье нет подробного описания, как работает rest и базы данных, а только то, что непосредственно относится к работам со StreamedContent.

Для того, чтобы прочитать касающиеся фронтенда стоит перейти на вторую часть этой статьи: 

https://www.tune-it.ru/web/marina/blog/-/blogs/streameddata-frontend
 

0. Что важно знать

 

StreamedContent - это потоковый формат данных, для загрузки различных файлов и других потоковых данных. StreamedContent используется в primefaces для того, чтобы работать с файлами, отображать изображения, загружать и скачивать различные данные.
 

О чтении из StreamedContent

 

Потоковые данные в java хранятся и передаются при помощи StreamedContent. Этот класс необходимую информацию о файле или других данных потокового типа. В числе этих данных - название, тип, кодировка и размер файла и поток данных типом InputStream.

public interface StreamedContent {
   String getName();

   InputStream getStream();

   String getContentType();

   String getContentEncoding();

   Integer getContentLength();
}

Самое главное, что нужно знать при работе со StreamedContent - вычитать байты из InputStream мы можем только один раз. При необходимости использования данных несколько раз, можно сохранить данные в массив байтов и потом создавать из него новые стримы. 
 

Для того, чтобы узнать, были ли вычитаны данные из стрима, в InputStream хранится количество доступных байт, получить его можно, воспользовавшись методом available() в Inputstream, а полное количество байт - в сontentLength у StreamedContent. 
 

StreamedContent stream = getStream(); // допустим, у нас есть какой-то стрим
stream.getContentLength(); // общее количество байтов в стриме
stream.getStream().available(); // количество доступных (невычитанных) байт

 

1. Скачивание файлов через REST

 


В общем-то получить картинку/файл можно точно также, как и текстовый ответ или json, только в этом случае нужно получить общий ответ в виде ResponseBody и из него взять StreamedContent. 

В своем примере я пользуюсь retrofit. Я написала метод для получения фотографии пользователя, который возвращает ResponseBody:
 

@GET("api/v/user/{id}/photo")
Call<ResponseBody> getUserPhoto(
       @Path("id") String userId
);

 

В ResponseBody хранится вся необходимая нам информация о файле: тип контента, размер и данные.
 

public abstract MediaType contentType();
public abstract long contentLength();
public final InputStream byteStream();

 

Как оговаривалось раньше, получить данные из byteStream() можно лишь единожды, но, если необходимо использовать эти данные несколько раз, то нужно сохранить данные в массив байтов. 

Ниже указан пример сохранения данных в переменную:
 

// Получаем файл
ResponceBody res = getUserPhoto(id); 
// Получаем размер файла
int available = (int) res.contentLength();
// Создаем массив и выделяем память
byte[] bytes = new byte[available];
// Записываем байты в массив
IOUtils.readFully(res.byteStream(), bytes);

 

После выполнения этого кода в res.byteStream() ничего не останется.

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

Воспользуемся builder’om в DefaultStreamedContent для создания стрима: 
 

StreamedContent stream = DefaultStreamedContent.builder()
       .stream(() -> new ByteArrayInputStream(bytes))
       .contentLength((int) res.contentLength())
       .contentType(StringUtil.safeToString(res.contentType()))
       .build();

2.  Проверка расширения файла

 

В некоторых случаях необходимо знать расширение файла, например, чтобы провести проверку по типу или для реализации разной логики для разных типов файлов.

Для того, чтобы проверить тип файла есть метод:
 

String mimeType = URLConnection.guessContentTypeFromStream(stream);

Пример ответа: “image/jpg”
 

guessContentTypeFromStream отдает строковое описание типа файла.  

Чаще всего, необходимых типов файлов больше, чем один. Когда мы ожидаем получить изображение, оно может быть сразу нескольких типов, например, одновременно gif, jpg, jpeg и png

Для того, чтобы проверить, имеет ли файл одно из необходимых расширений можно проверить тип с использованием regex-паттернов. Для этого можно написать regex-правило, по которому будет проверятся, подходит ли нам данный файл.

Продолжим пример с изображением. В этом случае regex-правило будет выглядеть вот так:
 

".*(gif|jpe?g|png)$" -- паттерн для фотографий типа gif, jpg, jpeg и png

 

Дальше дело за малым. Проверяем, подойдет ли тип под наше правило.
 

Pattern p = Pattern.compile(".*(gif|jpe?g|png)$"); // Создать паттерн
if (!p.matcher(mimeType).matches()) { // проверить на совпадение
   log.error("Incorrect file type");
   return null;
}

 

3. Сохранение файлов в базу и скачивание их из базы

 

Способов сохранить файл в базу множество. Поэтому расскажу тут один из возможных способов. Создадим таблицу для хранения файла.
 

create table file
(
    file_name varchar(255),
    file_media_type varchar(255),
    file_content bytea
);

 

Сущность, для описания этого файла

public class Document {
   private String name;
   private String mediaType;
   private byte[] content;
}

 

И репозиторий, для взаимодействия с базой

public interface FileRepository extends CrudRepository<File, Long> 
{
}


Отличия в загрузки в базу данных обычных данных от StreamedContent’a нет. Разница только лишь в типе сохраняемых данных.
 

fileRepository.save(file); // сохранение файла в базу
fileRepository.findAll(); // скачивание всех файлов
fileRepository.findByName(fileName); // cкачивание конкретного файла

 

Заключение

На этом закончена часть работы с файлами на бэкенде. Здесь мы научились скачивать файлы по рест апи, сохранять файлы и вынимать из базы данных, работать с расширением файла. В следующей части будет описана работа со StreamedContent на стороне фронта с использованием primefaces. [ссылка]

См. также 

2 часть статьи: 

https://www.tune-it.ru/web/marina/blog/-/blogs/streameddata-frontend

Документация:

https://www.primefaces.org/docs/api/8.0/org/primefaces/model/StreamedContent.html