如何将文件字段(HTML 表单)上传到数据库?

huangapple go评论78阅读模式
英文:

How to upload file field (html form) to database?

问题

我有一个包含上传文件字段的HTML表单(Thymeleaf)。在模型对象中,字段file设置如下:

@Entity
@Table(name = "services")
public class Service {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "service_id")
  private Long serviceId;

  @Size(min = 5, message = "Please enter 5 symbols or more")
  @NotBlank(message = "This field is mandatory")
  private String event;

  @NotNull(message = "This field is mandatory")
  @DateTimeFormat(pattern = "yyyy-MM-dd")
  private LocalDate created;

  @DateTimeFormat(pattern = "yyyy-MM-dd")
  private LocalDate modified;

  private BigDecimal price;

  @Lob
  private byte[] file;
  ...

控制器:

@PostMapping("/services")
public String addEvent(
    @Valid @ModelAttribute("service") Service service,
    BindingResult bindingResult,
    Model model
) {
  if (bindingResult.hasErrors()) {
    return "service-add-event";
  } else {
    serviceRepository.save(service);
  }
  List<Service> services = serviceRepository.findAll();
  model.addAttribute("services", services);
  return "redirect:/services";
}

视图:

<form action="#" method="post" th:action="@{/services}" th:object="${service}">
  <div class="form-group">
    <label for="event">Event *</label>
    <input th:class="${#fields.hasErrors('event')} ? 'form-control is-invalid' : 'form-control'"
      id="event" th:field="*{event}" type="text">
    <div class="invalid-feedback" th:if="${#fields.hasErrors('event')}" th:errors="*{event}">Name Error</div>
  </div>

  ...

  <div class="form-group">
    <label for="maintenanceFile">Maintenance File</label>
    <input class="form-control" id="maintenanceFile" th:field="*{file}" type="file">
  </div>
  <button class="btn btn-primary" type="submit">Save</button>
</form>

在将数据保存到数据库(嵌入式H2数据库)后,我看到仅保存了文件名到file字段(例如Testfile.txt),而不是整个文件。为什么?

如何将整个文件保存到数据库?

英文:

I have html form (Thymeleaf) that contains field for uploading file.

如何将文件字段(HTML 表单)上传到数据库?

In model object, field file is set as below:

@Entity
@Table(name = &quot;services&quot;)
public class Service {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = &quot;service_id&quot;)
  private Long serviceId;

  @Size(min = 5, message = &quot;Please enter 5 symbols or more&quot;)
  @NotBlank(message = &quot;This field is mandatory&quot;)
  private String event;

  @NotNull(message = &quot;This field is mandatory&quot;)
  @DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)
  private LocalDate created;

  @DateTimeFormat(pattern = &quot;yyyy-MM-dd&quot;)
  private LocalDate modified;

  private BigDecimal price;

  @Lob
  private byte[] file;
  ...

Controller:

@PostMapping(&quot;/services&quot;)
  public String addEvent(
      @Valid @ModelAttribute(&quot;service&quot;) Service service,
      BindingResult bindingResult,
      Model model
  ) {
    if (bindingResult.hasErrors()) {
      return &quot;service-add-event&quot;;
    } else {
      serviceRepository.save(service);
    }
    List&lt;Service&gt; services = serviceRepository.findAll();
    model.addAttribute(&quot;services&quot;, services);
    return &quot;redirect:/services&quot;;
    }
  }

View:

  &lt;form action=&quot;#&quot; method=&quot;post&quot; th:action=&quot;@{/services}&quot; th:object=&quot;${service}&quot;&gt;
    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label for=&quot;event&quot;&gt;Event *&lt;/label&gt;
      &lt;input th:class=&quot;${#fields.hasErrors(&#39;event&#39;)} ? &#39;form-control is-invalid&#39; : &#39;form-control&#39;&quot; 
id=&quot;event&quot; th:field=&quot;*{event}&quot; type=&quot;text&quot;&gt;
      &lt;div class=&quot;invalid-feedback&quot; th:if=&quot;${#fields.hasErrors(&#39;event&#39;)}&quot; th:errors=&quot;*{event}&quot;&gt;Name Error&lt;/div&gt;
    &lt;/div&gt;

    ...

    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label for=&quot;maintenanceFile&quot;&gt;Maintenance File&lt;/label&gt;
      &lt;input class=&quot;form-control&quot; id=&quot;maintenanceFile&quot; th:field=&quot;*{file}&quot; type=&quot;file&quot;&gt;
    &lt;/div&gt;
    &lt;button class=&quot;btn btn-primary&quot; type=&quot;submit&quot;&gt;Save&lt;/button&gt;

After saving to database (H2 embedded), I see that only filename is saved to field file (for example Testfile.txt), not the whole file. Why?

How to save the whole file to database?

答案1

得分: 1

我从其他开发人员那里得到了一些建议,并解决了这个问题。

  1. 首先,需要将enctype="multipart/form-data"添加到form标签中。

  2. 其次,需要将表单对象分为两部分。服务实例带有简单字段(字段file将为'null')。还有多部分输入标签。这是必需的,因为多部分是一个流(不是一个字段),应该在控制器中单独处理。

  3. 然后,在控制器中,我们从多部分中获取字节数组,将这些字节设置为服务对象字段,并将实例保存到仓库。

代码示例:

视图:

<form action="#" method="post" enctype="multipart/form-data" th:object="${service}" th:action="@{/services}">
    <div class="form-group">
      <label for="event">Event *</label>
      <input th:class="${#fields.hasErrors('event')} ? 'form-control is-invalid' : 'form-control'"
             id="event" th:field="*{event}" type="text">
      <div class="invalid-feedback" th:if="${#fields.hasErrors('event')}" th:errors="*{event}">
        Name Error
      </div>
      
      <!-- (...) -->

      <div class="form-group">
        <label for="maintenanceFile">Maintenance File</label>
        <input class="form-control" id="maintenanceFile" name="maintenanceFile" type="file">
      </div>
      <button class="btn btn-primary" type="submit">Save</button>
      <a class="btn btn-primary" role="button" th:href="@{/services}">Cancel</a>
  </form>

控制器:

@PostMapping(value = "/services", consumes = {"multipart/form-data"})
  public String addEvent(
      @Valid @ModelAttribute("service") Service service,
      BindingResult bindingResult,
      @RequestParam("maintenanceFile") MultipartFile maintenanceFile,
      Model model
  ) {
    if (bindingResult.hasErrors()) {
      return "service-add-event";
    } else {
      if (!maintenanceFile.isEmpty()) {
        try {
          byte[] fileBytes = maintenanceFile.getBytes();
          service.setFile(fileBytes);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      serviceRepository.save(service);
      List<Service> services = serviceRepository.findAll();
      model.addAttribute("services", services);
      return "redirect:/services";
    }
  }

注意:这是翻译后的代码和注释,用于说明原始代码中的逻辑。

英文:

I've got some advises from other developers and solved this issue.

  1. First, need to put enctype=&quot;multipart/form-data&quot; into form tag.

  2. Second, need to divide form objects into 2 parts. Service instance with simple fields (field file would be 'null'). And multipart input tag. This is needed, because multipart is a stream (not a field), and should be handled in Controller separately.

  3. Then, in Controller we get byte array from multipart, set this bytes to Service object field, and save the instance to Repository.

Code example:

View:

&lt;form action=&quot;#&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot; th:object=&quot;${service}&quot; 
th:action=&quot;@{/services}&quot;&gt;
    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label for=&quot;event&quot;&gt;Event *&lt;/label&gt;
      &lt;input th:class=&quot;${#fields.hasErrors(&#39;event&#39;)} ? &#39;form-control is-invalid&#39; : &#39;form-control&#39;&quot;
             id=&quot;event&quot; th:field=&quot;*{event}&quot; type=&quot;text&quot;&gt;
      &lt;div class=&quot;invalid-feedback&quot; th:if=&quot;${#fields.hasErrors(&#39;event&#39;)}&quot; th:errors=&quot;*{event}&quot;&gt;
        Name Error
      &lt;/div&gt;

    (...)

    &lt;div class=&quot;form-group&quot;&gt;
      &lt;label for=&quot;maintenanceFile&quot;&gt;Maintenance File&lt;/label&gt;
      &lt;input class=&quot;form-control&quot; id=&quot;maintenanceFile&quot; name=&quot;maintenanceFile&quot; type=&quot;file&quot;&gt;
    &lt;/div&gt;
    &lt;button class=&quot;btn btn-primary&quot; type=&quot;submit&quot;&gt;Save&lt;/button&gt;
    &lt;a class=&quot;btn btn-primary&quot; role=&quot;button&quot;
       th:href=&quot;@{/services}&quot;&gt;Cancel&lt;/a&gt;
  &lt;/form&gt;

Controller:

@PostMapping(value = &quot;/services&quot;, consumes = {&quot;multipart/form-data&quot;})
  public String addEvent(
      @Valid @ModelAttribute(&quot;service&quot;) Service service,
      BindingResult bindingResult,
      @RequestParam(&quot;maintenanceFile&quot;) MultipartFile maintenanceFile,
      Model model
  ) {
    if (bindingResult.hasErrors()) {
      return &quot;service-add-event&quot;;
    } else {
      if (!maintenanceFile.isEmpty()) {
        try {
          byte[] fileBytes = maintenanceFile.getBytes();
          service.setFile(fileBytes);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
      serviceRepository.save(service);
      List&lt;Service&gt; services = serviceRepository.findAll();
      model.addAttribute(&quot;services&quot;, services);
      return &quot;redirect:/services&quot;;
    }
  }

huangapple
  • 本文由 发表于 2020年9月28日 20:44:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/64102429.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定