【问题标题】:Preserve metadata for file upload in Spring Boot在 Spring Boot 中为文件上传保留元数据
【发布时间】:2022-01-07 04:16:20
【问题描述】:

我正在创建一个图像上传应用程序,用户可以在其中编辑图像元数据。我目前处理文件上传的方式是使用多部分文件。

fun handleFileUpload(@RequestParam files: Array<MultipartFile>): String {

这可行,但我无法访问图像的所有文件元数据。我想知道这样做的理想方法是什么,以便我可以访问元数据?我知道 Java.io.File 类具有元数据访问权限。

我更愿意在我的 Angular 前端保留拖放功能。

【问题讨论】:

    标签: spring spring-boot kotlin file-upload


    【解决方案1】:

    文件元数据不是图像元数据...

    在java中我们可以:

    对于 Kotlin(后端),我发现:

    让我们尝试/比较一下:

    package com.satckoverflow.imagemeta
    
    import com.drew.imaging.ImageMetadataReader
    import org.springframework.boot.autoconfigure.SpringBootApplication
    import org.springframework.boot.runApplication
    import org.springframework.stereotype.Controller
    import org.springframework.ui.Model
    import org.springframework.web.bind.annotation.GetMapping
    import org.springframework.web.bind.annotation.PostMapping
    import org.springframework.web.bind.annotation.RequestParam
    import org.springframework.web.multipart.MultipartFile
    import org.w3c.dom.NamedNodeMap
    import org.w3c.dom.Node
    import javax.imageio.ImageIO
    import javax.imageio.ImageReader
    import kotlin.text.StringBuilder
    
    
    fun main(args: Array<String>) {
        runApplication<ImageMetaDataApplication>(*args)
    }
    
    @SpringBootApplication
    @Controller("/")
    class ImageMetaDataApplication {
    
        @GetMapping
        fun show(): String {
            return "index"
        }
    
        @PostMapping
        fun handleFileUpload(@RequestParam files: Array<MultipartFile>, model: Model): String {
            val images = ArrayList<MyImage>()
            for (file in files) {
                // adopted from http://johnbokma.com/java/obtaining-image-metadata.html
                val iis = ImageIO.createImageInputStream(file.inputStream)
                val readers: Iterator<ImageReader> = ImageIO.getImageReaders(iis)
                if (readers.hasNext()) {
                    // pick the first available ImageReader
                    val reader = readers.next()
                    // attach source to the reader
                    reader.setInput(iis, true)
                    // read metadata of first image
                    val metadata = reader.getImageMetadata(0)
                    val names = metadata.metadataFormatNames
                    val length = names.size
                    for (i in 0 until length) {
                        images.add(MyImage(file.originalFilename, names[i], displayMetadata(metadata.getAsTree(names[i]))))
                    }
                }
            }
            model.addAttribute("images", images)
            return "index"
        }
    
        @PostMapping(params = ["3rdparty"])
        fun handleFileUploadScrimage(@RequestParam files: 
            Array<MultipartFile>, model: Model): String {
            val images = ArrayList<MyImage>()
            for (file in filesS) {
              val meta = ImageMetadataReader.readMetadata(file.inputStream, file.size)
              meta.directories.forEach { dir ->
                dir.tags.forEach { tag ->
                    images.add(MyImage(file.originalFilename, dir.name, tag.toString()))
                }
              }
            }
            model.addAttribute("images", images)
            return "index"
        }
        
    }
    
    // adopted from http://johnbokma.com/java/obtaining-image-metadata.html
    private fun displayMetadata(root: Node): String {
        val sb = StringBuilder()
        displayMetadata(root, 0, sb)
        return sb.toString()
    }
    
    private fun indent(level: Int, sb: StringBuilder) {
        for (i in 0 until level) sb.append("  ")
    }
    
    private fun displayMetadata(node: Node, level: Int, sb: StringBuilder): String {
        // print open tag of element
        if (level > 0) {
            sb.append(System.lineSeparator())
        }
        indent(level, sb)
        sb.append('<').append(node.nodeName)
        val map: NamedNodeMap = node.attributes
        // print attribute values
        val length = map.length
        for (i in 0 until length) {
            val attr: Node = map.item(i)
            sb.append(' ').append(attr.nodeName).append('=').append('"').append(attr.nodeValue).append('"')
        }
        var child = node.firstChild
        if (child == null) {
            // no children, so close element and return
            sb.append("/>")
        } else {
            // children, so close current tag
            sb.append('>')
            while (child != null) {
                // print children recursively
                displayMetadata(child, level + 1, sb)
                child = child.nextSibling
            }
            // print close tag of element
            sb.append(System.lineSeparator())
            indent(level, sb)
            sb.append("</").append(node.nodeName).append('>')
        }
        return sb.toString()
    }
    // adopted-end
    
    data class MyImage(val name: String?, val key: String?, val vali: String?)
    

    使用这个 src/main/resources/templates/index.html(对不起 thymeleaf,“没有设计”,只有 2 个输入..):

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
      <head>
        <title>Image meta data upload Example</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <style>
          table td, table td * {
            vertical-align: top;
          }
          pre {
            white-space: pre-wrap;
          }
    
        </style>
      </head>
      <body>
        <h2>Standard java</h2>
        <form action="#" method="post" th:action="@{/}" enctype="multipart/form-data">
          <label for="files0">File 1:</label>
          <input type="file" id="files0" name="files" accept="image/*"/>
          <label for="files1">File 2:</label>
          <input type="file" id="files1" name="files" accept="image/*"/>
          <input type="submit"/>
        </form>
        <hr/>
        <h2>additional lib</h2>
        <form action="#" method="post" th:action="@{/}" enctype="multipart/form-data">
          <label for="files2">File 1:</label>
          <input type="file" id="files2" name="files" accept="image/*"/>
          <label for="files3">File 2:</label>
          <input type="file" id="files3" name="files" accept="image/*"/>
          <input type="submit"/>
          <input type="hidden" name="3rdparty" />
        </form>
        <hr/>
        <table th:if="${images}">
          <thead>
            <tr>
              <th>Name</th>
              <th>Meta-Data-Key</th>
              <th>Meta-Data-Value</th>
            </tr>
          </thead>
          <tbody>
            <tr th:each="img: ${images}">
              <td th:text="${img.name}">some file</td>
              <td th:text="${img.key}">some text</td>
              <td>
                <pre th:text="${img.vali}">some text</pre>
              </td>
            </tr>
          </tbody>
        </table>
      </body>
    </html>
    

    我们可以得到一些东西,比如(没有额外的库,但有一些 xml-crawling 代码):

    还有:

    <dependency>
      <groupId>com.drewnoakes</groupId>
      <artifactId>metadata-extractor</artifactId>
      <version>2.16.0</version>
    </dependency>
    

    我们得到(几行)更像这样的东西:

    感谢blog javax.imageio“爬行”。

    【讨论】:

    • 我不知道文件和图像元数据是不同的东西。
    • 谢谢接受! :) 任何文件都有元数据(路径、名称、大小、日期、“类型”、访问/所有者/组属性......等等......有些也是特定于操作系统的),但图像元数据......相当具体。 .and 正如您所见,甚至特定于图像格式 (jpg/gif/...)
    猜你喜欢
    • 2017-05-24
    • 2021-06-09
    • 2021-01-26
    • 2020-05-23
    • 2018-10-08
    • 2017-12-30
    • 2019-04-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多