拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 数据库存储文件并且进行索引

数据库存储文件并且进行索引

白鹭 - 2021-11-24 429 0 0

1.概述

在构建某种内容管理解决方案时,我们需要解决两个问题。我们需要一个地方来存储文件本身,并且需要某种数据库来对它们进行索引。

可以将文件的内容存储在数据库本身中,或者我们可以将内容存储在其他位置并用数据库建立索引。

在本文中,我们将通过基本的Image Archive Application演示这两种方法。我们还将实现用于上传和下载的REST API。

2.用例

我们的图像存档应用程序将允许我们上传和下载JPEG图像

当我们上传图像时,应用程序将为其创建唯一的标识符。然后,我们可以使用此标识符下载它。

我们将使用带有Spring Data JPA和Hibernate的关系数据库。

3.数据库存储

让我们从我们的数据库开始。

3.1 图像实体

首先,让我们创建Image实体:

@Entity

 class Image {



 @Id

 @GeneratedValue

 Long id;



 @Lob

 byte[] content;



 String name;

 // Getters and Setters

 }

id字段用@GeneratedValue注释。这意味着数据库将为我们添加的每条记录创建一个唯一的标识符。通过使用这些值索引图像,我们无需担心同一图像的多次上传会相互冲突。

其次,我们有Hibernate @Lob批注。这就是我们告诉JPA我们打算存储潜在的大型二进制文件的方式

3.2 Image Repository

接下来,我们需要一个存储库以连接到数据库

我们将使用spring JpaRepository

@Repository

 interface ImageDbRepository extends JpaRepository<Image, Long> {}

现在我们准备保存图像。我们只需要一种将它们上传到我们的应用程序中的方法。

4. REST控制器

我们将使用MultipartFile上传图像。上载将返回imageId我们以后可以使用它来下载图像。

4.1 图片上传

让我们开始创建我们的ImageController以支持上传:

@RestController

 class ImageController {



 @Autowired

 ImageDbRepository imageDbRepository;



 @PostMapping

 Long uploadImage(@RequestParam MultipartFile multipartImage) throws Exception {

 Image dbImage = new Image();

 dbImage.setName(multipartImage.getName());

 dbImage.setContent(multipartImage.getBytes());



 return imageDbRepository.save(dbImage)

 .getId();

 }

 }

MultipartFile对象包含文件的内容和原始名称。我们使用它来构造Image像以存储在数据库中。

该控制器返回生成的id作为其响应的主体。

4.2 图片下载

现在,让我们添加一个下载路径:

@GetMapping(value = "/image/{imageId}", produces = MediaType.IMAGE_JPEG_VALUE)

 Resource downloadImage(@PathVariable Long imageId) {

 byte[] image = imageRepository.findById(imageId)

 .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND))

 .getContent();



 return new ByteArrayResource(image);

 }

imageId路径变量包含在上载时生成的ID。如果提供的ID无效,那么我们将使用ResponseStatusException返回HTTP响应代码404(未找到)。否则,我们会将存储的文件字节包装在ByteArrayResource ,以便下载它们。

5. 数据库映像存档测试

现在,我们可以测试图像存档了。

首先,让我们构建应用程序:

mvn package

其次,让我们开始吧:

java -jar target/image-archive-0.0.1-SNAPSHOT.jar

5.1 图片上传测试

应用程序运行后,我们将使用curl命令行工具上传图像

curl -H "Content-Type: multipart/form-data" \

 -F "[email protected]" http://localhost:8080/image

由于上传服务的响应为imageId ,这是我们的第一个请求,因此输出为:

1

5.2 图片下载测试

然后,我们可以下载图像:

curl -v http://localhost:8080/image/1 -o image.jpeg

-o image.jpeg选项将创建一个名为image.jpeg的文件,并将响应内容存储在其中:

% Total % Received % Xferd Average Speed Time Time Time Current

 Dload Upload Total Spent Left Speed

 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Trying ::1...

 * TCP_NODELAY set

 * Connected to localhost (::1) port 8080 (#0)

 > GET /image/1 HTTP/1.1

 > Host: localhost:8080

 > User-Agent: curl/7.54.0

 > Accept: */*

 >

 < HTTP/1.1 200

 < Accept-Ranges: bytes

 < Content-Type: image/jpeg

 < Content-Length: 9291

我们获得了HTTP / 1.1 200,这意味着我们的下载成功。

我们也可以尝试通过点击http://localhost:8080/image/1在浏览器中下载图像。

6.内容和位置分开

到目前为止,我们已经能够在数据库中上载和下载图像。

另一个不错的选择是将文件内容上传到其他位置。然后,我们仅将其文件系统location**保存**在DB中

为此,我们需要向Image实体添加一个新字段:

String location;

这将包含某些外部存储中文件的逻辑路径。在我们的例子中,它将是服务器文件系统上的路径。

但是,我们可以将此想法同样应用到不同的商店。例如,我们可以使用云存储– Google Cloud Storage或Amazon S3。该位置也可以使用URI格式,例如s3://somebucket/path/to/file

我们的上载服务不是将文件的字节写入数据库,而是将文件存储在适当的服务(在本例中为文件系统)中,然后将文件的位置放入数据库中。

7.文件系统存储

让我们将在文件系统中存储图像的功能添加到我们的解决方案中。

7.1 保存在文件系统中

首先,我们需要将图像保存到文件系统中:

@Repository

 class FileSystemRepository {



 String RESOURCES_DIR = FileSystemRepository.class.getResource("/")

 .getPath();



 String save(byte[] content, String imageName) throws Exception {

 Path newFile = Paths.get(RESOURCES_DIR + new Date().getTime() + "-" + imageName);

 Files.createDirectories(newFile.getParent());



 Files.write(newFile, content);



 return newFile.toAbsolutePath()

 .toString();

 }

 }

一个重要的注意事项–我们需要确保我们的每个图像在上传时都在服务器端定义了唯一的location 。否则,我们的上传内容可能会相互覆盖。

相同的规则适用于任何云存储,我们应该在其中创建唯一的密钥。在此示例中,我们将以毫秒格式将当前日期添加到图像名称:

/workspace/archive-achive/target/classes/1602949218879-baeldung.jpeg

7.2 从文件系统检索

现在,让我们实现代码以从文件系统中获取图像:

FileSystemResource findInFileSystem(String location) {

 try {

 return new FileSystemResource(Paths.get(location));

 } catch (Exception e) {

 // Handle access or file not found problems.

 throw new RuntimeException();

 }

 }

在这里,我们**使用其location**查找图像。然后,我们返回FileSystemResource

另外,我们正在捕获读取文件时可能发生的任何异常。我们可能还希望抛出具有特定HTTP状态的异常。

7.3 数据流和Spring的资源

我们的findInFileSystem方法返回FileSystemResource ,这是Spring的Resource接口的实现。

仅当我们使用文件时,它才会开始读取文件。在我们的例子中,将是通过RestController将其发送到客户端的时候。同样,它将把文件内容从文件系统流传输到用户,从而避免了将所有字节加载到内存中的麻烦

这种方法是将文件流式传输到客户端的良好通用解决方案。如果使用的是云存储而不是文件系统,则可以将FileSystemResource替换为另一个资源的实现,例如InputStreamResourceByteArrayResource

8.连接文件内容和位置

现在我们有了FileSystemRepository,我们需要将其链接到ImageDbRepository.

8.1。保存在数据库和文件系统中

让我们创建一个FileLocationService ,从保存流程开始:

@Service

 class FileLocationService {



 @Autowired

 FileSystemRepository fileSystemRepository;

 @Autowired

 ImageDbRepository imageDbRepository;



 Long save(byte[] bytes, String imageName) throws Exception {

 String location = fileSystemRepository.save(bytes, imageName);



 return imageDbRepository.save(new Image(imageName, location))

 .getId();

 }

 }

首先,我们将图像保存在文件系统中。然后,将包含其location的记录保存在数据库中

8.2 从数据库和文件系统检索

现在,让我们创建一个使用其id查找图片的方法:

FileSystemResource find(Long imageId) {

 Image image = imageDbRepository.findById(imageId)

 .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));



 return fileSystemRepository.findInFileSystem(image.getLocation());

 }

首先,我们在数据库中查找图像。然后我们获取它的位置并从文件系统中获取它

如果imageId ,则使用ResponseStatusException返回HTTP Not Found响应.

9.文件系统上传和下载

最后,让我们创建FileSystemImageController:

@RestController

 @RequestMapping("file-system")

 class FileSystemImageController {



 @Autowired

 FileLocationService fileLocationService;



 @PostMapping("/image")

 Long uploadImage(@RequestParam MultipartFile image) throws Exception {

 return fileLocationService.save(image.getBytes(), image.getOriginalFilename());

 }



 @GetMapping(value = "/image/{imageId}", produces = MediaType.IMAGE_JPEG_VALUE)

 FileSystemResource downloadImage(@PathVariable Long imageId) throws Exception {

 return fileLocationService.find(imageId);

 }

 }

首先,我们使新路径以“ / file-system ”开头。

然后,我们创建了与ImageController类似的上载路由,但没有dbImage对象。

最后,我们有下载路径,该路径使用FileLocationService查找图像并返回FileSystemResource作为HTTP响应。

10.文件系统映像存档测试

现在,我们可以像使用数据库版本一样测试文件系统版本,尽管路径现在以“ file-system ”开头:

curl -H "Content-Type: multipart/form-data" \

 -F "[email protected]" http://localhost:8080/file-system/image



 1

然后我们下载:

curl -v http://localhost:8080/file-system/image/1 -o image.jpeg

11.结论

在本文中,我们学习了如何将文件信息保存在数据库中,文件内容位于同一行或位于外部位置。

我们还使用分段上传来构建和测试REST API,并使用Resource提供了下载功能,以允许将文件流式传输到调用方。

标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *