Как обслуживать файлы изображений для внешнего интерфейса Spring Data REST? - PullRequest
1 голос
/ 19 июня 2019

Я пытаюсь создать веб-приложение базы данных фильмов.Каждый фильм должен иметь изображение постера.Я не знаю, как правильно подавать изображения на внешний интерфейс с помощью Spring Data REST.

Movie.java

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.io.File;
import java.sql.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Data
@Entity
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Movie {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String director;
    private Date releaseDate;
    private File posterFile;

    @ManyToMany
    @JoinTable(
            name = "MOVIE_GENRES",
            joinColumns = @JoinColumn(name = "MOVIE_ID"),
            inverseJoinColumns = @JoinColumn(name = "GENRE_ID"))
    private Set<Genre> genres = new HashSet<>();

    @OneToMany
    @MapKeyColumn(name = "ACTOR_ROLE")
    private Map<String, Actor> cast = new HashMap<>();

    public Movie(String title) {
        this.title = title;
    }

    public void addActor(String role, Actor actor) {
        cast.put(role, actor);
    }

    public void removeActor(String role) {
        cast.remove(role);
    }

    public void addGenre(Genre genre) {
        genres.add(genre);
    }

    public void removeGenre(Genre genre) {
        genres.remove(genre);
    }
}

Я не могу использовать байт.массив в компоненте фильма, поскольку он слишком велик для сохранения в базе данных.Вместо этого я мог бы сохранить объект File, объект Path или строку, содержащую путь: private File posterFile; Проблема в том, что он сохранит локальный путь, например "C:\user\documents\project\backend\images\posterxyz.png".Когда я пытаюсь использовать этот путь в качестве img-src в моем интерфейсе, он получает ошибку «Не разрешено загружать локальный ресурс».Я имею в виду, это звучит как глупый способ сделать это в любом случае.Я просто не знаю, как правильно это сделать.

Это хранилище фильмов.Я использую Spring Data REST в бэкэнде, который генерирует JSON в формате языка приложений Hypermedia.

MovieRepository.java

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = "movies", path = "movies")
public interface MovieRepository extends PagingAndSortingRepository<Movie, Long> {

}

Ответы [ 2 ]

1 голос
/ 20 июня 2019

На самом деле это невозможно с Spring Data / REST, поскольку он фокусируется на структурированных данных;т.е. таблицы и ассоциации по большей части.Да, есть несколько обручей, через которые вы могли бы перейти, как объяснено в других ответах, но есть также связанный проект под названием Spring Content , который решает именно эту проблемную область.

Spring Content предоставляет те же парадигмы программирования, что и Spring Data / REST, только для неструктурированных данных;т. е. изображения, документы, фильмы и т. д. Таким образом, используя этот проект, вы можете связать один или несколько «контентных» объектов с объектами Spring Data и управлять ими через HTTP, как и с вашими объектами Spring Data.

Довольнопросто добавить в проект, как показано ниже:

pom.xml (также доступны загрузчики)

   <!-- Java API -->
   <dependency>
      <groupId>com.github.paulcwarren</groupId>
      <artifactId>spring-content-jpa</artifactId>
      <version>0.9.0</version>
   </dependency>
   <!-- REST API -->
   <dependency>
      <groupId>com.github.paulcwarren</groupId>
      <artifactId>spring-content-rest</artifactId>
      <version>0.9.0</version>
   </dependency>

Конфигурация

@Configuration
@EnableJpaStores
@Import("org.springframework.content.rest.config.RestConfiguration.class")
public class ContentConfig {

    // schema management (assuming mysql)
    // 
    @Value("/org/springframework/content/jpa/schema-drop-mysql.sql")
    private Resource dropContentTables;

    @Value("/org/springframework/content/jpa/schema-mysql.sql")
    private Resource createContentTables;

    @Bean
    DataSourceInitializer datasourceInitializer() {
        ResourceDatabasePopulator databasePopulator =
                new ResourceDatabasePopulator();

        databasePopulator.addScript(dropContentTables);
        databasePopulator.addScript(createContentTables);
        databasePopulator.setIgnoreFailedDrops(true);

        DataSourceInitializer initializer = new DataSourceInitializer();
        initializer.setDataSource(dataSource());
        initializer.setDatabasePopulator(databasePopulator);

        return initializer;
    }
}

Чтобы связать контент, добавьте аннотации Spring Content к вашей сущности Movie.

Movie.java

@Entity
public class Movie {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;
    .. existing fields...    
  // private File posterFile; no longer required

  @ContentId
  private String contentId;

  @ContentLength
  private long contentLength = 0L;

  // if you have rest endpoints
  @MimeType
  private String mimeType = "text/plain";
}

Создайте «хранилище»:

MoviePosterContentStore.java

@StoreRestResource(path="moviePosters")
public interface MoviePosterContentStore extends ContentStore<Movie, String> {
}

Это все, что вам нужно для создания конечных точек REST @ /moviePosters.Когда ваше приложение запускается, Spring Content проверит ваши зависимости с помощью Spring Content JPA, взглянет на ваш MoviePosterContentStore интерфейс и внедрит реализацию этого интерфейса для JPA.Он также увидит зависимость Spring Content REST и внедрит реализацию @Controller, которая перенаправляет HTTP-запросы в ваш MoviePosterContentStore.Это избавляет вас от необходимости реализовывать все это самостоятельно, что, я думаю, то, что вы ищете.

Итак ...

Для управления контентом с помощью внедренного API REST:

curl -X POST /moviePosters/{movieId} -F file=@/path/to/poster.jpg

сохранит изображение в базе данных (в виде большого двоичного объекта) и свяжет его с сущностью фильма с идентификатором movieId.

curl /moviePosters/{movieId} -H "Accept: image/jpeg"

получит его снова и так далее ... поддерживает все методы CRUD и потоковое видео. BTW!

Есть пара руководств по началу работы здесь .Справочное руководство по JPA здесь .И здесь есть обучающее видео здесь .Бит кодирования начинается примерно на полпути.

Пара дополнительных моментов: - если вы используете Spring Boot Starters, то вам по большей части не нужна @Configuration.
- Простокак Spring Data - это абстракция, так же как и Spring Content, так что вы не ограничены хранением изображений постеров в виде больших двоичных объектов в базе данных.Вы можете хранить их в файловой системе или в облачном хранилище, таком как S3, или в любом другом хранилище, поддерживаемом Spring Content.

HTH

1 голос
/ 19 июня 2019

Я бы:

Один

предотвращает сериализацию атрибута posterFile, добавляя аннотацию @JsonIgnore в поле.

@JsonIgnore
private File posterFile;

Вы также можете сделать это через смешанный класс Джексона, чтобы избежать «загрязнения» ваших сущностей инструкциями по обработке Json, но вам нужно будет исследовать это самостоятельно.

Два

Добавьте пользовательскую ссылку в представление ресурса, которая позволит клиентам получать данные изображения по требованию. например /movies/21/poster

Подробнее о том, как добавить пользовательские ссылки на ресурс, см. Здесь:

Пользовательские ссылки остальных данных Spring на ресурсе

И специально для создания ссылки на Spring MVC Controller:

https://docs.spring.io/spring-hateoas/docs/0.24.0.RELEASE/api/org/springframework/hateoas/mvc/ControllerLinkBuilder.html

https://stackoverflow.com/a/24791083/1356423

Три

Создайте стандартный контроллер Spring MVC, связанный с путем, на который указывает пользовательская ссылка, и который будет считывать данные файла и передавать ответ.

, например

@Controller
public MoviePosterController{

    @GetMapping(path="/movies/{movieId}/poster")
    //https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web for auto resolution of path var to domain Object
    public @ResponseBody byte[] getPoster(@PathVariable("movieId") Movie movie, HttpServletResponse response){
        File file = movie.getPosterFile();
        //stream the bytes of the file
        // see https://www.baeldung.com/spring-controller-return-image-file
        // see https://www.baeldung.com/spring-mvc-image-media-data
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...