Однажды я столкнулся с той же проблемой, когда я не совсем привык к the life in the IoC world
. Поле @Autowired
одного из моих bean-компонентов является нулевым во время выполнения.
Основная причина заключается в том, что вместо использования автоматически созданного компонента, поддерживаемого контейнером Spring IoC (чье поле @Autowired
равно indeed
] правильно введенный), я newing
мой собственный экземпляр этого типа бобов и его использование. Конечно, это поле @Autowired
равно нулю, потому что Spring не имеет возможности его ввести.
, если вы используете версию Spring версии 3.1 или новее, вы можете указать «производит» в аннотации @RequestMapping
. Пример ниже работает для меня из коробки. Нет необходимости в конвертере регистров или что-либо еще, если вы используете веб-mvc (@EnableWebMvc
).
@ResponseBody
@RequestMapping(value = "/photo2", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
Весной 4 очень легко вам не нужно вносить какие-либо изменения в фасоль. Отметьте свой тип возврата только @ResponseBody.
Пример: -
@RequestMapping(value = "/image/{id}")
public @ResponseBody
byte[] showImage(@PathVariable Integer id) {
byte[] b;
/* Do your logic and return
*/
return b;
}
Вы должны указать тип материала в ответе. Я использую аннотацию @GetMapping с результатами = MediaType.IMAGE_JPEG_VALUE. @RequestMapping будет работать одинаково.
@GetMapping(value="/current/chart",produces = MediaType.IMAGE_JPEG_VALUE)
@ResponseBody
public byte[] getChart() {
return ...;
}
Без типа носителя трудно догадаться, что на самом деле возвращается (включая любого, кто читает код, браузер и, конечно же, сама Весна). Байт [] просто не определен. Единственный способ определить тип носителя из байта [] - это обнюхивание и угадывание вокруг.
Предоставление типа носителя - это наилучшая практика
Это работает для меня весной 4.
@RequestMapping(value = "/image/{id}", method = RequestMethod.GET)
public void findImage(@PathVariable("id") String id, HttpServletResponse resp){
final Foto anafoto = <find object>
resp.reset();
resp.setContentType(MediaType.IMAGE_JPEG_VALUE);
resp.setContentLength(anafoto.getImage().length);
final BufferedInputStream in = new BufferedInputStream(new ByteArrayInputStream(anafoto.getImageInBytes()));
try {
FileCopyUtils.copy(in, resp.getOutputStream());
resp.flushBuffer();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
С Spring 4.1 и выше вы можете возвращать почти все что угодно (например, картинки, PDF-файлы, документы, банки, молнии и т. д.) довольно просто без каких-либо дополнительных зависимостей. Например, следующим способом может быть способ возврата изображения профиля пользователя из MongoDB GridFS:
@RequestMapping(value = "user/avatar/{userId}", method = RequestMethod.GET)
@ResponseBody
public ResponseEntity<InputStreamResource> downloadUserAvatarImage(@PathVariable Long userId) {
GridFSDBFile gridFsFile = fileService.findUserAccountAvatarById(userId);
return ResponseEntity.ok()
.contentLength(gridFsFile.getLength())
.contentType(MediaType.parseMediaType(gridFsFile.getContentType()))
.body(new InputStreamResource(gridFsFile.getInputStream()));
}
Что нужно отметить:
С помощью этого метода вам не нужно беспокоиться об автоподготовке в HttpServletResponse, бросая IOException или копируя данные потока.
Я думаю, вам, возможно, понадобится служба для хранения файлов и получения этого файла. Подробнее см. здесь
1) Создать хранилище хранения
@Service
public class StorageService {
Logger log = LoggerFactory.getLogger(this.getClass().getName());
private final Path rootLocation = Paths.get("upload-dir");
public void store(MultipartFile file) {
try {
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
} catch (Exception e) {
throw new RuntimeException("FAIL!");
}
}
public Resource loadFile(String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return resource;
} else {
throw new RuntimeException("FAIL!");
}
} catch (MalformedURLException e) {
throw new RuntimeException("FAIL!");
}
}
public void deleteAll() {
FileSystemUtils.deleteRecursively(rootLocation.toFile());
}
public void init() {
try {
Files.createDirectory(rootLocation);
} catch (IOException e) {
throw new RuntimeException("Could not initialize storage!");
}
}
}
2) Создать Rest Controller для загрузки и получения файла
@Controller
public class UploadController {
@Autowired
StorageService storageService;
List<String> files = new ArrayList<String>();
@PostMapping("/post")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
String message = "";
try {
storageService.store(file);
files.add(file.getOriginalFilename());
message = "You successfully uploaded " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (Exception e) {
message = "FAIL to upload " + file.getOriginalFilename() + "!";
return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(message);
}
}
@GetMapping("/getallfiles")
public ResponseEntity<List<String>> getListFiles(Model model) {
List<String> fileNames = files
.stream().map(fileName -> MvcUriComponentsBuilder
.fromMethodName(UploadController.class, "getFile", fileName).build().toString())
.collect(Collectors.toList());
return ResponseEntity.ok().body(fileNames);
}
@GetMapping("/files/{filename:.+}")
@ResponseBody
public ResponseEntity<Resource> getFile(@PathVariable String filename) {
Resource file = storageService.loadFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + file.getFilename() + "\"")
.body(file);
}
}
Не ответы на меня работали, поэтому мне удалось это сделать следующим образом:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("your content type here"));
headers.set("Content-Disposition", "attachment; filename=fileName.jpg");
headers.setContentLength(fileContent.length);
return new ResponseEntity<>(fileContent, headers, HttpStatus.OK);
Настройка заголовка Content-Disposition
Мне удалось загрузить файл с помощью аннотации @ResponseBody
по моему методу.
Используя Spring 3.1.x и 3.2.x, вы должны это сделать:
Метод контроллера:
@RequestMapping("/photo2")
public @ResponseBody byte[] testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
return IOUtils.toByteArray(in);
}
И аннотация mvc в сервлет- Файл context.xml:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.ByteArrayHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>image/jpeg</value>
<value>image/png</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
В дополнение к нескольким ответам здесь несколько указателей (Spring 4.1).
Если у вас нет каких-либо messageconverters, настроенных в вашем WebMvcConfig, если ResponseEntity
внутри вашего @ResponseBody
работает хорошо .
Если вы это сделаете, то есть у вас есть MappingJackson2HttpMessageConverter
, настроенный (например, я), используя ResponseEntity
, возвращает org.springframework.http.converter.HttpMessageNotWritableException
.
Единственным рабочим решением в этом случае является для обертывания byte[]
в @ResponseBody
следующим образом:
@RequestMapping(value = "/get/image/{id}", method=RequestMethod.GET, produces = MediaType.IMAGE_PNG_VALUE)
public @ResponseBody byte[] showImageOnId(@PathVariable("id") String id) {
byte[] b = whatEverMethodUsedToObtainBytes(id);
return b;
}
В этом случае нужно правильно настроить messageconverters (и добавить ByteArrayHttpMessageConverer
) в свой WebMvcConfig, например:
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(mappingJackson2HttpMessageConverter());
converters.add(byteArrayHttpMessageConverter());
}
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(objectMapper);
return converter;
}
@Bean
public ByteArrayHttpMessageConverter byteArrayHttpMessageConverter() {
ByteArrayHttpMessageConverter arrayHttpMessageConverter = new ByteArrayHttpMessageConverter();
arrayHttpMessageConverter.setSupportedMediaTypes(getSupportedMediaTypes());
return arrayHttpMessageConverter;
}
private List<MediaType> getSupportedMediaTypes() {
List<MediaType> list = new ArrayList<MediaType>();
list.add(MediaType.IMAGE_JPEG);
list.add(MediaType.IMAGE_PNG);
list.add(MediaType.APPLICATION_OCTET_STREAM);
return list;
}
В вашем контексте приложения объявите AnnotationMethodHandlerAdapter и registerByteArrayHttpMessageConverter:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<util:list>
<bean id="byteArrayMessageConverter" class="org.springframework.http.converter.ByteArrayHttpMessageConverter"/>
</util:list>
</property>
</bean>
также в методе обработчика задайте соответствующий тип контента для вашего ответа.
Помимо регистрации ByteArrayHttpMessageConverter
, вы можете использовать ResponseEntity
вместо @ResponseBody
. Для меня работает следующий код:
@RequestMapping("/photo2")
public ResponseEntity<byte[]> testphoto() throws IOException {
InputStream in = servletContext.getResourceAsStream("/images/no_image.jpg");
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(IOUtils.toByteArray(in), headers, HttpStatus.CREATED);
}
Я предпочитаю этот вариант:
private ResourceLoader resourceLoader = new DefaultResourceLoader();
@ResponseBody
@RequestMapping(value = "/{id}", produces = "image/bmp")
public Resource texture(@PathVariable("id") String id) {
return resourceLoader.getResource("classpath:images/" + id + ".bmp");
}
Измените тип носителя на то, что у вас есть.
ResourceLoader
, но построение пути от внешнего ввода, как в вашем примере, является плохой идеей: cwe.mitre.org/data/definitions/22.html
– qerub
14 November 2016 в 23:07
@RequestMapping(value = "/get-image",method = RequestMethod.GET)
public ResponseEntity<byte[]> getImage() throws IOException {
RandomAccessFile f = new RandomAccessFile("/home/vivex/apache-tomcat-7.0.59/tmpFiles/1.jpg", "r");
byte[] b = new byte[(int)f.length()];
f.readFully(b);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.IMAGE_PNG);
return new ResponseEntity<byte[]>(b, headers, HttpStatus.CREATED);
}
Работал для меня.
Так я делаю это с помощью Spring Boot и Guava:
@RequestMapping(value = "/getimage", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public void getImage( HttpServletResponse response ) throws IOException
{
ByteStreams.copy( getClass().getResourceAsStream( "/preview-image.jpg" ), response.getOutputStream() );
}