【问题标题】:Store pictures in H2 database spring boot thymleaf将图片存储在H2数据库spring boot thymeleaf中
【发布时间】:2019-02-06 10:15:28
【问题描述】:

美好的一天。我想将图像存储在 h2 数据库中,然后在 html 页面中检索并显示相同的图像。我正在使用spring boot和文件上传方法,但是绑定结果出现错误

这里是页面/类:

Category.java

package com.vishal.project.entities;

@Entity
@Table(name="category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="ID")
private Long id;

@Size(min=1, max=90)
@Column(name="CATEGORY_NAME")
private String CategoryName;


@Lob
@Column(name="CATEGORY_PHOTO")
private byte[] CategoryPhoto;


public Category(Long id, @Size(min = 1, max = 90) String categoryName, byte[] categoryPhoto) {
    super();
    this.id = id;
    CategoryName = categoryName;
    CategoryPhoto = categoryPhoto;
}

public byte[] getCategoryPhoto() {
    return CategoryPhoto;
}

public void setCategoryPhoto(byte[] categoryPhoto) {
    CategoryPhoto = categoryPhoto;
}

public Category() {}

@OneToMany(mappedBy = "category", cascade=CascadeType.ALL, orphanRemoval=true)
private Set<Book> Books = new HashSet<>();

public Set<Book> getBooks() {
    return Books;
}

public void setBooks(Set<Book> books) {
    Books = books;
}

public Long getId() {
    return id;
}
public void setCategoryID(Long id) {
    this.id = id;
}
public String getCategoryName() {
    return CategoryName;
}
public void setCategoryName(String categoryName) {
    CategoryName = categoryName;
}

@Override
public String toString() {

    return "Category ID:" + id + 
           "Category Name:"+ CategoryName;
}


}

Categorycontroller.java

package com.vishal.project.web;


import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.vishal.project.entities.Category;
import com.vishal.project.services.CategoryService;
import com.vishal.project.util.Message;


@Controller
@RequestMapping(value="/categories")
public class CategoryController {


private final Logger logger = LoggerFactory.getLogger(BookController.class);

@Autowired
private MessageSource  messageSource;

@Autowired 
private CategoryService categoryService;

@GetMapping
public String list(Model uiModel) {
    logger.info("Listing categories:");
    List<Category> categories = categoryService.findALL();
    uiModel.addAttribute("categories", categories);
    logger.info("No. of categories: " + categories.size());
    return "categories";
}

@GetMapping(value = "/{id}" , consumes="Multipart/formdata")
public String show(@PathVariable Long id, Model model) {
    Category category = categoryService.findbyID(id);


    model.addAttribute("category", category);
    return "showCategory";
}

@GetMapping(value = "/edit/{id}")
public String updateForm(@PathVariable Long id, Model model) {
    model.addAttribute("category", categoryService.findbyID(id));
    return "updateCategory";
}

@GetMapping(value = "/new")
public String create(Model uiModel) {
    logger.info("creating Category ...");
    Category category = new Category();     

    uiModel.addAttribute("category", category);
    return "updateCategory";
}


@PostMapping(value = "/upload")
public String saveCategory(@Valid @ModelAttribute("category") Category category, BindingResult bindingResult,
        Model uiModel, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes,
        Locale locale, @RequestParam(value="file", required=true) MultipartFile file) {
    logger.info("Creating Category....");
    logger.info("Category ID" + category.getId());
    logger.info("Category ID" + category.getCategoryName());
    logger.info("Category ID" + category.getCategoryPhoto());
    if(bindingResult.hasErrors())
    {
        logger.info("Error:", bindingResult.getAllErrors());
        logger.debug("field Error:", bindingResult.getFieldError());
        uiModel.addAttribute("message", new Message("error", messageSource.getMessage("category_save_fail", new Object[] {}, locale)));
        uiModel.addAttribute("category", category);
        return "updateCategory";
    }
    uiModel.asMap().clear();
    redirectAttributes.addFlashAttribute("message", 
            new Message("success", messageSource.getMessage("Category_save_success", new Object[] {}, locale)));
    //process upload file 
    logger.info("File Name :", file.getName() );
    logger.info("File Size :", file.getSize() );
    logger.info("File content type :", file.getContentType() );
    if(file != null) {
    byte[] filecontent = null;
    try
    {
        InputStream inputStream = file.getInputStream();
        if(inputStream == null) 
            logger.debug("file InputStream is null");
        filecontent = IOUtils.toByteArray(inputStream);     
        category.setCategoryPhoto(filecontent);
    }catch(IOException ex) {
        logger.error("Error Saving uploaded file");
    }
    category.setCategoryPhoto(filecontent);
    }

    categoryService.save(category);
    return "redirect:/categories/" + category.getId().toString();
}

}

categoryShow.page

<body>
<div th:replace="fragments/header_admin :: header_admin">Header</div>
<div class="container">

<h1>Category Details</h1>

<div>
    <form class="form-horizontal" th:object="${category}" >
    <input type="hidden" th:field="*{id}"/>
        <div class="form-group">
            <label class="col-sm-2 control-label">Category Name:</label>
            <div class="col-sm-10">
                <p class="form-control-static" th:text="${CategoryName}"> 
</p></div>
        </div>

        <div class="form-group">
            <label class="col-sm-2 control-label" >Category Photo</label>
            <div class="col-sm-10">
                <p class="form-control-static" ><img alt="CatName" 
  th:src="@{CategoryPhoto}" /> </p></div>
        </div>
    </form>
</div>

categoryUpdate 页面(创建或更新具有详细信息和图像的类别)

<div class="container">

<h1>Category Details</h1>

<div>
    <form  class="form-horizontal" th:object="${category}" th:action="@{/categories/upload}" method="post" enctype="multipart/form-data">
        <input type="hidden" th:field="*{id}"/>
        <div class="form-group">
            <label class="col-sm-2 control-label">Category Name</label>
            <div class="col-sm-10">
                <input class="form-control" th:field="*{CategoryName}"/>
            </div>
        </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">Category Photo</label>
            <div class="col-sm-10">
                <input name="file" type="file" class="form-control" th:field="*{CategoryPhoto}"/>
            </div>
        </div>
        <div class="row">
            <button class="btn btn-default">Save</button>
        </div>
    </form>
</div>
<div th:insert="~{fragments/footer :: footer}">&copy; 2017 Iuliana Cosmina & Apress</div>

错误:我进入 CategoryController.saveCategory() 方法的 bindingResult。

当我调试代码时,我得到了错误。这是一张图片来演示:

我很难使用 thymleaf 在 CategoryShow 页面上显示图像。 任何帮助将不胜感激。

更新:谁能告诉我这个错误是什么意思,请:

Failed to convert property value of    type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'byte[]' for property 'CategoryPhoto'; nested exception is 
java.lang.IllegalArgumentException: Cannot convert value of type 'org.springframework.web.multipart.support.
StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'byte' for property
'CategoryPhoto[0]': PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor] returned 
inappropriate value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' 

**最终更新:我收到此错误:** 所需的请求部分“文件”不存在

【问题讨论】:

  • 你遇到了什么错误?
  • 你可以使用多部分来做到这一点,Here's 一个例子。
  • 请不要发布错误图片。在您的问题中粘贴堆栈跟踪或错误消息日志。
  • 要检索它,只需将该字节数组从数据库转换为 Base64string,角度可以直接读取它&lt;img ng-src="'data:image/png;base64,' + {{yourbase64String}}" &gt;
  • 似乎问题在于您的对象需要一个字节[],但帖子是一个多部分文件。您应该创建一个 CategoryDTO 对象,其中包含您的验证注释,但仅适用于非文件字段。然后在您的控制器中使用经过验证的值创建一个新类别,然后执行 c.setCategoryPhoto(file.getBytes()) 然后持久化。因为您有表单字段和对象字段具有相同的名称,它试图转换但不能

标签: java spring-mvc spring-boot thymeleaf h2


【解决方案1】:

问题已解决只需告诉您我在案例中做了什么:

1) 如果您在页面中上传一个文件,则在控制器方法中使用 Part 文件作为您的帖子映射的参数

2) 对于我的第二个问题,图像没有使用 thymeleaf 在 html 页面中呈现,因为我使用 byte[] 来持久保存在我的数据库中。所以百里香没有解决。所以我使用 Apache commons 二进制编解码器 Base64(可以添加为 Gradle 或 Maven 依赖项)将我的 byte[] 图像转换为 Base64 字符串,以便它可以由 thymleaf 解析。 像这样:

    Category category = categoryService.findbyID(id);
    byte[] image = category.getCategoryPhoto();
    String CatImage =  Base64.encodeBase64String(image);

然后在 HTML 中

<img  th:src="@{'data:image/jpeg;base64,'+${Cimage}}" />

希望这对某人有帮助!谢谢。

【讨论】:

    【解决方案2】:

    Spring 将上传的文件转换为 MultipartFile 对象,因此您不能将其直接映射到字节数组。

    您可以使用 MultipartFile#getBytes() 从 MultipartFile 中获取字节数组。

    在您的情况下,您可以使用中间对象(如CategoryForm),其中字段CategoryPhoto 的类型为MultipartFile。 然后,在您的控制器中,使用我上面显示的方法将其映射到您已经拥有的 Category 对象。

    【讨论】:

    • 我明白您要提出的观点,但为什么它说所需的文件“不存在”,因为我正在上传 JPG 文件。从您的回答中,它应该会给出错误,例如无法映射对象或类似的东西。
    • 我认为 spring 无法识别或找到我上传的文件
    • 您对之前的示例进行了哪些更改以得到这个新错误?
    • 刚刚将我的控制器方法 savecategory 的参数从多部分文件更改为部分文件,因为我一次上传一个文件
    【解决方案3】:

    您好,您的 Thymeleaf 表单解析如下,如果存在 th:field 属性,则删除 name 属性

    <form class="form-horizontal" action="/categories/upload" method="post" enctype="multipart/form-data">
        <input id="id" name="id" value="" type="hidden">
        <div class="form-group">
            <label class="col-sm-2 control-label">Category Name</label>
            <div class="col-sm-10">
                <input class="form-control" id="CategoryName" name="CategoryName" value="">
            </div>
        </div>
          <div class="form-group">
            <label class="col-sm-2 control-label">Category Photo</label>
            <div class="col-sm-10">
                <input name="CategoryPhoto" class="form-control" id="CategoryPhoto" type="file">
            </div>
        </div>
        <div class="row">
            <button class="btn btn-default">Save</button>
        </div>
    </form>
    

    这清楚地说明了您遇到的错误,即使您在文件输入中提到 name="file" 它也被解析为 name=categoryPhoto,因为名称标签后提到了 th:field="*{CategoryPhoto}"。

    使用 th:value="${product.name}" th:name="name" th:id="name" 代替 th:field 会更灵活

    【讨论】:

      【解决方案4】:

      您始终可以做的是将工作文件上传example 与您的文件进行比较。

      另一件事有助于将您的输入名称与您的控制器方法期望您的文件的名称进行比较。

      如果您发布的代码仍然相关,您可以在模板中的文件输入中找到名称“Fileimport”,但在您的控制器中您需要文件(@RequestParam(value="file", required=false))。

      其他有助于调试的事情:

      • 使用浏览器的开发者工具查看您通过网络发送的内容
      • 在服务器端记录incoming requests(要么这样,要么对你来说太复杂,你可以简单地遍历参数名称并记录它们(如果可能,也记录它们的值)

      如果这对您没有帮助,那么请更新帖子:更新您的代码(模板+控制器,如果已更改)并使用更好的堆栈跟踪:更好我的意思是您不应该只显示最后 N 行stacktrace,但至少到执行通过您的代码的第一行(换句话说,类名以您的包开头),如果第一个 Caused by 或第二个有意义,则更好。

      【讨论】: