英文:
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.
In model object, field file
is set as below:
@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;
...
Controller:
@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";
}
}
View:
<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>
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
我从其他开发人员那里得到了一些建议,并解决了这个问题。
-
首先,需要将
enctype="multipart/form-data"
添加到form
标签中。 -
其次,需要将表单对象分为两部分。服务实例带有简单字段(字段
file
将为'null')。还有多部分输入标签。这是必需的,因为多部分是一个流(不是一个字段),应该在控制器中单独处理。 -
然后,在控制器中,我们从多部分中获取字节数组,将这些字节设置为服务对象字段,并将实例保存到仓库。
代码示例:
视图:
<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.
-
First, need to put
enctype="multipart/form-data"
intoform
tag. -
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. -
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:
<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>
Controller:
@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";
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论