หลังจากโดนมรสุมงานหนักมาตั้งแต่ต้นปีครับ กว่าจะได้กลับบ้านที่ก็ปาไป 3 ทุ่มกว่าๆ ถึง 4 ทุ่มและ เริ่มกลับมาทำ Thesis สักทีครับ สำหรับโจทย์ที่ผมเอามาเขียน Blog ครั้งนี้เป็นการ Upload File จากนั้น Save ที่ไฟล์ที่ Server ครับ พร้อมกับบันทึกข้อมูลที่ป้อนเข้ามาจาก User ครับ
เตรียมตัว
- Tools พร้อม Code พร้อมครับ สำหรับผมใช้ Code ที่ตั้งโครงจาก Blog ก่อนๆมาพัฒนาต่อครับ
Just Coding ครับ
- กำหนด Dependency ที่เกี่ยวข้องกันก่อนในไฟล์ pom.xml ครับ
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>nz.net.ultraq.thymeleaf</groupId> <artifactId>thymeleaf-layout-dialect</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
- มากำหนด application.properties ครับ
#เปิด Option การใช้ Multipart spring.http.multipart.enabled=true #กำหนด Part ที่ Upload File bpmn.upload.path = D:\\BPMN\\99Web\\WuMuBPMN\\src\\main\\resources\\static\\BPMNUpload #กำหนดรูปแบบไฟล์ที่สามารถ Upload ได้ bpmn.upload.extensions=bpmn
- กำหนด Entity ครับ
@Entity public class testItem{ @Id @GeneratedValue(strategy = GenerationType.AUTO) private int testItemId; //บอก Path ที่เก็บไฟล์ BPMN private String testItemPath; private String testItemName; @Transient private MultipartFile BPMNFile; //พวก getter และ setter ผมขอละไว้นะครับ }
สังเกตุดีๆ ผมมี Property ที่เกี่ยวกับ Multipart ชื่อ BPMNFile ครับ โดย Property นี้ ผมกำหนดเป็น @Transient ครับ ซึ่งเป็นตัวที่บอก Hibernate ว่าไม่ต้องสร้าง Column นี้ลงที่ฐานข้อมูลครับ
- สร้าง View ที่เกี่ยวข้องครับ - อันนี้ผมใช้ Thymeleaf Layout ครับ (สามารถศึกษาเพิ่มเติมได้จาก Blog ผมครับ อิอิ) แต่ผมใน Blog นี้ผมใส่เฉพาะ View ครับ
<!DOCTYPE html> <html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorator="layouts/mainLayout"> <head> <title>Layout</title> </head> <body> <div layout:fragment="content"> <h2>Test Item Detail</h2> <div> <form class="form-horizontal" th:object="${testitem}" th:action="@{/testitem}" method="post" enctype="multipart/form-data"> <!-- <input type="hidden" th:field="*{testitemid}"/> --> <div class="form-group"> <label class="col-sm-2 control-label">test item name:</label> <div class="col-sm-10"> <input type="text" class="form-control" th:field="*{testItemName}"/> <label th:if="${#fields.hasErrors('testItemName')}" th:class="'error'">test Item Name Error</label> </div> </div> <div class="form-group"> <label class="col-sm-2 control-label">test item file:</label> <div class="col-sm-10"> <input type="file" class="form-control" th:field="*{BPMNFile}"/> <label th:if="${#fields.hasErrors('testItemPath')}" th:class="'error'">test Item Path Error</label> </div> <label class="col-sm-2 control-label">test item path:</label> <div class="col-sm-10"> <input type="text" class="form-control" th:field="*{testItemPath}" readonly = true/> <label th:if="${#fields.hasErrors('testItemPath')}" th:class="'error'">test Item Path Error</label> </div> </div> <div class="form-group"> <button type="submit" class="btn btn-default">Submit</button> </div> </form> </div> </div> </body> </html>
- สังเกตุในส่วนของ <input type="file" class="form-control" th:field="*{BPMNFile}"/> ครับ ในส่วนของ th:field="*{BPMNFile}" ครับ ตรง BPMNFile จะสัมพันธ์กับในส่วนของ Entity ครับ ^__^
- สร้าง Controller กันครับ มีครบครันสำหรับ CRUD Operation ไปดู Code ได้เลยครับ
package com.cu.thesis.WuMuBPMN.controllers.manageTest; //ส่วนขอการ Import เชื่อใจใน Editor ครับ @Controller public class testItemController { //ดึงค่ามาจาก application.properties @Value("${bpmn.upload.path}") private String uploadPath; @Value("${bpmn.upload.extensions}") private String bpmnExtension; @Autowired private testItemService testItemServiceI; /* * ดึงข้อมูล TEst Case ทั้งหมด */ @RequestMapping(value = "testitems", method = RequestMethod.GET) public String listTestItem(Model model) { model.addAttribute("testitems", testItemServiceI.listAll()); System.out.println("Returning testitems:"); return "manageTest/testItemList"; } /** * ดูข้อมูล Test Item */ @RequestMapping(value = "testitem/{id}") public String viewTestItem(@PathVariable Integer id, Model model) { model.addAttribute("testitem", testItemServiceI.getById(id)); return "manageTest/viewTestItem"; } /** * Edit Entry สำหรับ Edit */ @RequestMapping(value = "testitem/edit/{id}") public String editPerson(@PathVariable Integer id, Model model) { model.addAttribute("testitem", testItemServiceI.getById(id)); return "manageTest/editTestItem.html"; } /** * new Entity สำหรับ New */ @RequestMapping(value = "testitem/new") public String newTestItem(Model model) { model.addAttribute("testitem", new testItem()); return "manageTest/editTestItem.html"; } /** * บันทึกข้อมูลลง DB */ @RequestMapping(value ="testitem", method = RequestMethod.POST) public String saveTestItem(@Valid testItem testItemEntry , BindingResult result) { //จัดการ File String FilePath; try { if(result.hasErrors()) { return "manageTest/editTestItem.html"; } FilePath = SaveBPMNFile(testItemEntry.getBPMNFile(), uploadPath); testItemEntry.setTestItemPath(FilePath); testItemServiceI.save(testItemEntry); } catch (IOException e) { e.printStackTrace(); } catch (FileUploadException e) { e.printStackTrace(); } return "redirect:/testitems"; } protected String SaveBPMNFile(MultipartFile file, String uploadDirectory) throws IOException, FileUploadException { String fileName = file.getOriginalFilename(); CheckValidFileExtension(file); Path path = Paths.get(uploadDirectory, fileName); Files.copy(file.getInputStream(), path); return uploadDirectory +"/"+ fileName; } protected void CheckValidFileExtension(MultipartFile file) throws FileUploadException { String extension = FilenameUtils.getExtension(file.getOriginalFilename()); if (!extension.equals(bpmnExtension)) { throw new FileUploadException("Not valid BPMN Extension"); } } @RequestMapping(value ="testitem/delete/{id}") public String deleteTestItem(@PathVariable Integer id) throws IOException { //ลบแล้ว ต้องเอาออกให้เกลี้ยงนะ testItem testItemEntry = testItemServiceI.getById(id); Files.deleteIfExists(Paths.get(testItemEntry.getTestItemPath())); testItemServiceI.delete(id); //กลับไปที่หน้าจอ List return "redirect:/testitems"; } }
- ส่วน Service / DAO ใช้จาก CRUDRepository ตามปกติครับ ผมเลยไม่ใส่ Code นะครับ
หน้าตาของระบบ (ผมไม่มีหัวการออกแบบเลยครับ 555)
- หน้า List
- ตอน Edit ครับ
ปัญหาที่พบหละ ?
- ปัญหา Exception: MultipartException: Current request is not a multipart request
แก้ไขโดย - ตรวจสอบที่ View ในส่วนของ Form ว่าลืมใส่ enctype="multipart/form-data" หรือป่าว - ปัญหา Required request part 'file' is not present
แก้ไขโดย - ตรวจสอบที่ View ว่ามีการผูก Mulitipart Element อย่าง เช่น File Upload หรือ <input type="file" class="form-control" th:field="*{BPMNFile}"/> ครับ ยังไม่ได้ผูกกับ Model (Entity) แต่ Method ใน Controller ครับ มัน Require ตัว Multipart ครับ ถ้าใน Thymeleaf เป็นส่วน th:field="*{BPMNFile}" ครับ
Discover more from naiwaen@DebuggingSoft
Subscribe to get the latest posts sent to your email.