Проблема с открытие файлов загруженных на сервер в Spring Boot
Есть Controller в котором через @GetMapping обрабатываю страницу
orders/{id}/upload
где реализован следующий функционал.
Выводится список файлов со ссылками на эти файлы из директории
src/main/resources/static/layout/{id}/
и реализована форма multipart form, через которою происходит загрузка файлов на сервер в указанную выше папку в RestController через @PostMapping.
Проблема такая:
Когда я загружаю файл на сервер, он попадает в список файлов, но если я перехожу по ссылке, выпадает ошибка 404, хотя файл на сервере есть.
Я перезагружаю сервер и ссылка начинает работать (адрес ссылки не меняется).
Подскажите, в чем может быть проблема?
@Controller
public class OrderController {
@GetMapping("/orders/{id}/upload")
public String upload(@PathVariable(value = "id") long id, Model model) {
model.addAttribute("order_number", id);
String directory = "src/main/resources/static/layout/" + id + "/";
File file = new File(directory);
if (!file.exists()) {
file.mkdirs();
}
File[] listFiles = file.listFiles();
model.addAttribute("listfile", listFiles);
return "upload";
}
}
.
@Service
public class FileUploadService {
public void uploadFile(MultipartFile file, long id) throws IOException {
File uploadFile = new File("/Users/prisukhin/Documents/printing/src/main/resources/static/layout/" + id + "/" + file.getOriginalFilename());
if(file != null) {
file.transferTo(uploadFile);
}
}
}
.
@RestController
public class FileUploadRestController {
@Autowired
FileUploadService fileUploadService;
@PostMapping("/orders/{id}/upload")
public RedirectView fileUpload(@PathVariable(value = "id") long id, @RequestParam MultipartFile file_form, Model model) throws IOException {
fileUploadService.uploadFile(file_form, id);
return new RedirectView("/orders/{id}/upload");
}
часть файла upload.html
<table class="table table-hover">
<tbody th:each="l : ${listfile}">
<tr>
<td>
<a th:text="${#strings.substringAfter(l, '/layout/'+order_number+'/')}" th:href="@{'../../'}+${#strings.substringAfter(l, '/static/')}" target="_blank"></a>.
</td>
</tr>
</tbody>
</table>
<form th:attr="action=@{'/orders/'} + ${order_number} + @{'/upload'}" method="POST" enctype="multipart/form-data" class="upload-form mx-auto">
<div class="input-group mb-3">
<input type="file" class="form-control" name="file_form" />
<div class="input-group-append">
<button class="btn btn-secondary" type="submit">Загрузить</button>
</div>
</div>
</form>
Whitelabel error page
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Fri Sep 23 11:50:06 MSK 2022
There was an unexpected error (type=Not Found, status=404).
No message available
Debug информация
2022-09-23 11:50:02.881 DEBUG 58415 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : POST "/orders/45/upload", parameters={multipart}
2022-09-23 11:50:02.947 DEBUG 58415 --- [nio-8080-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.prisukhin.integral.controllers.FileUploadRestController#fileUpload(long, MultipartFile, Model)
2022-09-23 11:50:03.024 DEBUG 58415 --- [nio-8080-exec-1] o.s.web.servlet.view.RedirectView : View [RedirectView], model {}
2022-09-23 11:50:03.038 DEBUG 58415 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed 302 FOUND
2022-09-23 11:50:03.046 DEBUG 58415 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : GET "/orders/45/upload", parameters={}
2022-09-23 11:50:03.047 DEBUG 58415 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.prisukhin.integral.controllers.OrderController#upload(long, Model)
2022-09-23 11:50:03.065 DEBUG 58415 --- [nio-8080-exec-2] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, application/xhtml+xml, image/avif, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8]
2022-09-23 11:50:03.668 DEBUG 58415 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : GET "/css/style.css", parameters={}
2022-09-23 11:50:03.672 DEBUG 58415 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : GET "/img/logo-1.png", parameters={}
2022-09-23 11:50:03.675 DEBUG 58415 --- [nio-8080-exec-3] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2022-09-23 11:50:03.675 DEBUG 58415 --- [nio-8080-exec-4] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2022-09-23 11:50:03.691 DEBUG 58415 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2022-09-23 11:50:03.693 DEBUG 58415 --- [nio-8080-exec-3] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2022-09-23 11:50:03.728 DEBUG 58415 --- [nio-8080-exec-4] o.s.web.servlet.DispatcherServlet : Completed 200 OK
2022-09-23 11:50:06.162 DEBUG 58415 --- [nio-8080-exec-5] **o.s.web.servlet.DispatcherServlet : GET "/layout/45/0000005z.xml", parameters={}
2022-09-23 11:50:06.163 DEBUG 58415 --- [nio-8080-exec-5] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2022-09-23 11:50:06.164 DEBUG 58415 --- [nio-8080-exec-5] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2022-09-23 11:50:06.165 DEBUG 58415 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND
2022-09-23 11:50:06.173 DEBUG 58415 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}
2022-09-23 11:50:06.177 DEBUG 58415 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
2022-09-23 11:50:06.226 DEBUG 58415 --- [nio-8080-exec-5] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, text/html;q=0.8]
2022-09-23 11:50:06.229 DEBUG 58415 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404**
Ответы (1 шт):
Папка /src/main/resources/ не предназначена для того чтобы в нее загружались пользовательские файлы.
Данная папка в исходниках предназначена для ресурсов приложения.
После сборки все содержимое данной папки перепещается в
- папку
target/classes
вместе с скомпилированными классами и импортированными зависимостями
(если проект собирается maven) - или в папку
build/resources
(если проект собирается gradle)
скомпилированные классы перемещаются вbuild/classes
откуда(target/build) и запускается приложение.
Именно поэтому вы ничего не видите до перезапуска.
Когда Вы делаете запрос /layout/45/0000005z.xml Ваш ResourceHttpRequestHandler пытается его найти не по пути /Users/prisukhin/Documents/printing/src/main/resources/static/layout/45/0000005z.xml (как Вы того ожидаете), а по пути
/Users/prisukhin/Documents/printing/target/static/layout/45/0000005z.xml
(если проект собирается maven)/Users/prisukhin/Documents/printing/build/resources/static/layout/45/0000005z.xml
(если проект собирается gradle)
Только умоляю Вас, не стоит ничего загружать
- ни в
target/classes/ - ни в
build/resources/
Они также не предназначены для заргузки пользовательских файлов.
Это временные папки для сборки проекта
Как поступить?
Вам нужно создать отдельную папку вне папки с исходными кодами и вне папок для сборки проекта.
Если хотите чтобы она находилась рядом, можете создать что-то вроде:
/Users/prisukhin/Documents/printing/storage/
И настроить отдачу статики на нее.
Для этого добавьте в файл настройки MVC (класс конфигурации имплементирующий WebMvcConfigurer) в метод addResourceHandlers(ResourceHandlerRegistry registry) следующие строки:
registry
.addResourceHandler("/storage/**")
.addResourceLocations("file:/Users/prisukhin/Documents/printing/storage/")
;
После чего нужно поправить контроллер
@Controller
public class OrderController {
@GetMapping("/orders/{id}/upload")
public String upload(@PathVariable(value = "id") long id, Model model) {
model.addAttribute("order_number", id);
String directory = "/Users/prisukhin/Documents/printing/storage/layout/" + id + "/";
File file = new File(directory);
if (!file.exists()) {
file.mkdirs();
}
File[] listFiles = file.listFiles();
model.addAttribute("listfile", listFiles);
return "upload";
}
}
Сервис
@Service
public class FileUploadService {
public void uploadFile(MultipartFile file, long id) throws IOException {
File uploadFile = new File("/Users/prisukhin/Documents/printing/storage/layout/" + id + "/" + file.getOriginalFilename());
if(file != null) {
file.transferTo(uploadFile);
}
}
}
ну и ссылки поправить...
Они должны будут ссылаться на /storage/layout/45/0000005z.xml
Конфигурирование
Данный же путь лучше положить в application.properties, добавив строку, наподобии:
app.storage.path=/Users/prisukhin/Documents/printing/storage/
После чего Вы сможете её внедрить в Сервис (и куда угодно) следующим образом
@Value("${app.storage.path}")
String storagePath;
В сервисе:
@Service
public class FileUploadService {
@Value("${app.storage.path}")
String storagePath;
public void uploadFile(MultipartFile file, long id) throws IOException {
File uploadFile = new File(storagePath + "layout/" + id + "/" + file.getOriginalFilename());
if(file != null) {
file.transferTo(uploadFile);
}
}
}
В контроллере:
@Controller
public class OrderController {
@Value("${app.storage.path}")
String storagePath;
@GetMapping("/orders/{id}/upload")
public String upload(@PathVariable(value = "id") long id, Model model) {
model.addAttribute("order_number", id);
String directory = storagePath + "layout/" + id + "/";
File file = new File(directory);
if (!file.exists()) {
file.mkdirs();
}
File[] listFiles = file.listFiles();
model.addAttribute("listfile", listFiles);
return "upload";
}
}
После чего вы сможете легко перемещать данное приложение не беспокоясь о том, что Ваше приложение прибито гвоздями к вашему личному компьютеру. Вы всегда сможете указать нужный путь для нужной среды и оно сможет работать одновременно как на вашем компьютере так и на сервере где нибудь в /var/www/