how to display image in a browser in spring boot using the image url - spring

I'm saving images to the database using spring boot, the image url is also displayed on postman when I send it but the issue is that when I copy the image url link to the browser, the image is not showing rather it's showing This application has no explicit mapping for /error, so you are seeing this as a fallback.What is wrong with it.
This is my Entity class
#Data
#Entity
#NoArgsConstructor
public class Attachment {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String fileName;
private String fileType;
#Lob
private byte[] data;
public Attachment(String fileName, String fileType, byte[] data) {
this.fileName = fileName;
this.fileType = fileType;
this.data = data;
}
}
This is the dto class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class AttachmentDto {
private String fileName;
private String downloadUrl;
private String fileType;
private long fileSize;
}
This is my controller class
#RestController
public class AttachmentController {
private AttachmentService attachmentService;
public AttachmentController(AttachmentService attachmentService) {
this.attachmentService = attachmentService;
}
#PostMapping("/upload")
public ResponseEntity<AttachmentDto> uploadFile(#RequestParam("file")MultipartFile file) throws Exception {
Attachment attachment = attachmentService.saveFile(file);
String downloadUrl = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/profileImage")
.path(attachment.getFileName())
.toUriString();
return new ResponseEntity<>(new AttachmentDto(attachment.getFileName(),downloadUrl,file.getContentType(),file.getSize()), HttpStatus.CREATED);
}
}
Here is my service class
#Service
public class AttachmentService {
private AttachmentRepository attachmentRepository;
public AttachmentService(AttachmentRepository attachmentRepository) {
this.attachmentRepository = attachmentRepository;
}
public Attachment saveFile(MultipartFile file) throws Exception {
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
if (fileName.contains("..")){
throw new Exception("File Name contains invalid sequence");
}
Attachment attachment = new Attachment(fileName,file.getContentType(),file.getBytes());
return attachmentRepository.save(attachment);
}catch (Exception e){
e.printStackTrace();
throw new Exception("Could not save file "+fileName);
}
}
}
The image was saved successfully in postman and this was returned
"fileName": "IMG_20180312_131559.jpg",
"downloadUrl": "http://localhost:8080/profileImageIMG_20180312_131559.jpg",
"fileType": "image/jpeg",
"fileSize": 1061724
I copied the downloadUrl into the browser but the image is not showing nor downloading but rather showing This application has no explicit mapping for /error, so you are seeing this as a fallback.
Mon Sep 26 11:11:52 PDT 2022
There was an unexpected error (type=Not Found, status=404).

Related

no String-argument constructor/factory method to deserialize from String value with spring boot client

I am using spring boot application with frontend (spring boot application using thymeleaf) and backend (spring boot REST application ) are separated using REST api. The frontend uses HttpClient to send request to backend. Whenever I try to update an object the HttpClient creates an error for json parsing. The request is not accepted by the backend (ProcessDTORequest object ) with error as follows.
The exception is as follows:
{"message":"JSON parse error: Cannot construct instance of `com.app.dataaccess.entity.Process` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('68d22e4d-7116-4130-aa06-9ba120aadc66'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.app.dataaccess.entity.Process` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('68d22e4d-7116-4130-aa06-9ba120aadc66')\n at [Source: (PushbackInputStream); line: 1, column: 10310] (through reference chain: com.app.ui.dto.request.ProcessDTORequest[\"answeredQuestionnaires\"]->java.util.HashSet[0]->com.app.dataaccess.entity.AnsweredQuestionnaire[\"process\"])","httpStatus":"INTERNAL_SERVER_ERROR","timeStamp":"2022-11-04T08:44:35.9108286Z"}
HttpClient method for post request is as follows:
public String executePost(
final String url, final Object payLoad, final Map<String, String> headers,
final Map<String, String> params) throws Exception {
final CloseableHttpClient httpClient = HttpClientBuilder.create().build();
// Add query strings to URL
URIBuilder builder = new URIBuilder(url);
for (final Map.Entry<String, String> elm : params.entrySet()) {
builder = builder.setParameter(elm.getKey(), elm.getValue());
}
// can change for HttpPut, HttpPost, HttpPatch
final HttpPost request = new HttpPost(builder.build());
// Add headers from input map
for (final Map.Entry<String, String> elm : headers.entrySet()) {
request.addHeader(elm.getKey(), elm.getValue());
}
request.setHeader("Accept", "application/json");
request.setHeader("Content-type", "application/json");
// Send Json String as body, can also send UrlEncodedFormEntity
final StringEntity entity = new StringEntity(objectMapper.writeValueAsString(payLoad));
request.setEntity(entity);
try {
final CloseableHttpResponse response = httpClient.execute(request);
System.out.println("Return response status code: "+response.getStatusLine().getStatusCode());
System.out.println("Return response status code: "+response.getStatusLine());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// Read response string using EntityUtils class of Apache http client library
// Serialize json string into map or any other object
return EntityUtils.toString(response.getEntity());
} else {
throw new Exception(EntityUtils.toString(response.getEntity()));
// throw new Exception(String.format("Response status code was and response was ",
// response.getStatusLine().getStatusCode(), EntityUtils.toString(response.getEntity())));
}
} catch (final ClientProtocolException e) {
throw new Exception("Client protocol Exception occurred while executing request", e);
} catch (final Exception e) {
System.out.println(e);
throw new Exception(e);
}
}
I used the configuration for object mapper as follows:
#Configuration
public class AppConfig {
#Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true);
objectMapper.registerModule(new JavaTimeModule());
return objectMapper; }
}
Process.java (this is used for serializing/deserializing)
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.UUIDGenerator.class)
public class Process {
private UUID processId;
private List<User> users = new ArrayList<>();
private List<UnitType> units = new ArrayList<>();
private String furtherComment;
private List<AnsweredQuestionnaire> answeredQuestionnaires = new ArrayList<>()
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Process)) return false;
Process process = (Process) o;
return getProcessId().equals(process.getProcessId());
}
#Override
public int hashCode() {
return Objects.hash(getProcessId());
}
}
The json from the server is like the following
{
"#id": "bba35e58-5d4b-44ce-9a5a-486f55f79af7",
"processId": "21ef7f9d-4fcc-417c-96e8-4327206d2592",
"users": [
{
"#id": "69d2f392-8213-4f34-9cb5-f0c403170787",
"userId": "5a17ec5f-c20a-4873-93af-bf69fad4eb26",
"roles": [
{
"roleId": "f6ad33a7-9d03-4260-81c2-a4a4c791e30a",
"users": []
}
],
"processes": []
}
],
"units": [
{
"unitTypeId": "c784d197-1dc7-446e-b3e5-6468a7954878",
"unit": {
"unitId": "aba76d05-e2ea-4b5a-828b-349966595258"
},
"isResponsibleUnit": true
}
],
"furtherComment": "",
"answeredQuestionnaires": [
{
"#id": "7ca1af09-eefd-4c56-9587-581858fbbc57"
}
]
}
The relation between the entities Process, AnsweredQuestionnaire and User is as follows:
Between Process and AnsweredQuestionnaire (One-to-many) respectively.
Between Process and User (many-to-many).
Between Process and UnitType (one-to-many) respectively.
AnsweredQuestionnaire.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class AnsweredQuestionnaire {
private UUID answeredQuestionnaireId;
private Questionnaire questionnaire;
private Process process;
public void addProcessToAnsweredQuestionnaire(Process process){
//remove old association
if(this.process != null){
this.process.getAnsweredQuestionnaires().remove(this);
}
this.process = process;
//add new association
if(process != null){
this.process.getAnsweredQuestionnaires().add(this);
}
}
}
User.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class User {
private UUID userId;
private String firstName;
private String lastName;
private String phoneNumber;
private String email;
private List<Role> roles = new ArrayList<>();
private List<Process> processes = new ArrayList<>();
public void addProcessToUser(Process process){
this.processes.add(process);
process.getUsers().add(this);
}
public void removeProcessFromUser(Process process){
this.processes.remove(process);
process.getUsers().remove(this);
}
}
ProcessDTORequest.java (this class is on the backend accepting the request from the frontend)
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class ProcessDTORequest {
private UUID processId;
private Set<User> users = new HashSet<>();
private Set<AnsweredQuestionnaire> answeredQuestionnaires = new HashSet<>();
private Set<UnitType> units = new HashSet<>();
}
UnitType.java
#Setter
#Getter
#NoArgsConstructor
#AllArgsConstructor
public class UnitType {
private UUID unitTypeId;
private Unit unit;
private Boolean isResponsibleUnit = false;
}

Handler Goblal Exceptions Spring - add data when sending exception

I have a doubt about how to pass more data to throw an exception, I want to pass more data at the time of launching it, to put that data in the service response ..
I have an exception handler class labeled #ControllerAdvice in spring, but I don't know the best way to pass the data.
This is the code I have
throw new OcspException("Exception OCSP");
public class OcspException extends RuntimeException {
private static final long serialVersionUID = 1L;
public OcspException(String businessMessage) {
super(businessMessage);
}
public OcspException(String businessMessage, Throwable throwable) {
super(businessMessage, throwable);
}
}
#ExceptionHandler(OcspException.class)
public ResponseEntity<Object> exception(OcspException exception,HttpServletRequest request) {
ResponseException response = new ResponseException();
response.setCode("404");
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
I have the idea to do it, but I don't know if it is a good practice ... in the OcspException class to create attributes with their setter and getters, and create the constructor that receives this data, to then extract the data in exception controller
throw new OcspException("Exception OCSP","Hello");
public class OcspException extends RuntimeException {
private static final long serialVersionUID = 1L;
private String m;
public OcspException(String businessMessage) {
super(businessMessage);
}
public OcspException(String businessMessage, Throwable throwable) {
super(businessMessage, throwable);
}
public OcspException(String businessMessage, String message) {
super(businessMessage);
setM(message);
}
public String getM() {
return m;
}
public void setM(String m) {
this.m = m;
}
}
#ExceptionHandler(OcspException.class)
public ResponseEntity<Object> exception(OcspException exception,HttpServletRequest request) {
ResponseException response = new ResponseException();
response.setCode("404");
response.setDetails(exception.getM() );
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
Try making an model called ErrorDetails which will hold a timestamp, message, and details.
It may look like this:
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class ErrorDetails {
private LocalDateTime timeStamp;
private String message;
private String details;
}
Here's a sample of what my custom exceptions usually look like:
#Data
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class OrderNotFoundException extends RuntimeException {
private final String message;
public OrderNotFoundException(String message) {
super(message);
this.message = message;
}
}
Then for the #ExceptionHandler:
#ExceptionHandler(OrderNotFoundException.class)
public ResponseEntity<ErrorDetails>
orderNotFoundException(OrderNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = ErrorDetails.builder()
.timeStamp(LocalDateTime.now())
.message(ex.getMessage())
.details(request.getDescription(false))
.build();
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
The error response for an order not found ends up being this:
{
"timeStamp": "2019-10-07T21:31:37.186",
"message": "Order with id 70 was not found.",
"details": "uri=/api/v1/order"
}
This way you can add whatever extra details in the ErrorDetails object. I hope that helps!

SpringBoot : getting error while uploading file

using spring-boot i m trying to post image but i got
org.thymeleaf.exceptions.TemplateInputException
entity class
#Entity
#Table(name="image")
public class ImageEntity {
#Id
#Column(name="imageId")
private String imageId;
#Column(name="imageName")
private String imageName;
#Column(name="type")
private String type;
/*#Column(name="size")
private long size;*/
#Column(name="imagepath")
private String path;
public ImageEntity(String imageName, String type, String path) {
super();
this.imageName = imageName;
this.type = type;
//this.size = size;
this.path = path;
}
Controller Class
#Controller
public class ImgContr {
public static final Logger logger =LoggerFactory.getLogger(ImgContr.class);
#Autowired
public ImgService imgService;
#PostMapping("/addImage")
public ImageEntity saveImage(#RequestBody ImageEntity imgent, RedirectAttributes redirectAttributes) throws Exception
{
return imgService.saveImage(imgent );
}
Domain Service
#Service
public class ImgService {
#Autowired
public ImageDao imageDao;
public ImageEntity saveImage(ImageEntity imgent) {
ImageEntity imgEngDom=new ImageEntity();
imgEngDom.setImageId(imgent.getImageId());
imgEngDom.setImageName( imgent.getImageName());
imgEngDom.setPath(imgent.getPath());
//imgEngDom.setSize(imgent.getSize());
imgEngDom.setType(imgent.getType());
return imageDao.saveImage(imgEngDom);
}
ImageDAO.java
#Repository
public class ImageDao {
#PersistenceContext
private EntityManager entityManager;
#Autowired
SessionFactory sessionFactory;
public ImageEntity saveImage(ImageEntity imgEngDom) {
Session session = null;
try {
session = sessionFactory.openSession();
session.beginTransaction();
session.save(imgEngDom);
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
} finally {
session.close();
}
return imgEngDom;
}
Payload Request.
{
"imageName": "Divya",
"type" : "jpg",
"path": " C:/Users/admin/Desktop"
}
//if i try to post image like this below in postman i got error
Error
{
"timestamp": 1548408353973,
"status": 500,
"error": "Internal Server Error",
"exception": "org.thymeleaf.exceptions.TemplateInputException",
"message": "Error resolving template \"addImage\", template might not
exist or might not be accessible by any of the configured Template
Resolvers",
"path": "/addImage"
}
I am new to springboot where i m wrong. Help me.
I think problem with your Controller configuration.
try this
#RestController
public class ImgContr {
instead of
#Controller
public class ImgContr {
For more in-depth details please follow >> Controller vs RestController
Note : Above solution works when you need a json response not a solution for Spring-mvc Projects.

Spring-boot MultipartFile issue with ByteArrayResource

I'm trying to implement a rest api consuming excel file. I'm using spring-boot and code is available here.
Code works fine when using FileSystemResource for payload. But i'm not able to make the code work with ByteArrayResource in replacement of FileSystemResource:
RestApi.java:
#RestController
public class RestApi {
private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
#PostMapping("/api/upload")
public ResponseEntity<?> uploadFile(#RequestParam("file") MultipartFile uploadfile) {
LOGGER.debug("Single file upload!");
try {
LOGGER.info("\n\n ****** File name: {}, type {}! ************", uploadfile.getOriginalFilename(), uploadfile.getContentType());
this.processExcelFile(uploadfile.getInputStream());
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<>("Successfully uploaded - " + uploadfile.getOriginalFilename(), new HttpHeaders(), HttpStatus.OK);
}
private List<String> processExcelFile(InputStream stream) throws Exception {
List<String> result = new ArrayList<String>();
//Create Workbook instance holding reference to .xlsx file
try(XSSFWorkbook workbook = new XSSFWorkbook(stream);) {
//Get first/desired sheet from the workbook
XSSFSheet sheet = workbook.getSheetAt(0);
//Iterate through each rows one by one
Iterator<Row> rowIterator = sheet.iterator();
while (rowIterator.hasNext()) {
Row row = rowIterator.next();
String cellValue = row.getCell(0).getRichStringCellValue().toString();
result.add(cellValue);
LOGGER.info("\n\n ****** Cell value: {} ************", cellValue);
}
return result;
}
}
}
RestApiTest:
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class RestApiTest {
#Autowired
private TestRestTemplate restTemplate;
#Autowired
private ResourceLoader loader;
#Test
public void testUploadFile() throws Exception {
Resource resource = this.loader.getResource("classpath:test.xlsx");
MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
// parts.add("file", new FileSystemResource(resource.getFile()));
parts.add("file", new ByteArrayResource(IOUtils.toByteArray(resource.getInputStream())));
String response = this.restTemplate.postForObject("/api/upload", parts, String.class);
Assertions.assertThat(response).containsIgnoringCase("success");
}
}
I'm getting following error when running test:
java.lang.AssertionError:
Expecting:
<"{"timestamp":1487852597527,"status":400,"error":"Bad Request","exception":"org.springframework.web.multipart.support.MissingServletRequestPartException","message":"Required request part 'file' is not present","path":"/api/upload"}">
to contain:
<"success">
(ignoring case)
Any idea?
when using loader.getResource(...) you must use resource itself as answered above. So you don't need ByteArrayResource. I got this problem, but I'm not using resource from classpath. So if someone really need to use ByteArrayResource, here is my workaround
public class FileNameAwareByteArrayResource extends ByteArrayResource {
private String fileName;
public FileNameAwareByteArrayResource(String fileName, byte[] byteArray, String description) {
super(byteArray, description);
this.fileName = fileName;
}
#Override
public String getFilename() {
return fileName;
}
}
and then use it
parts.add("file", new FileNameAwareByteArrayResource("filename", byteArray));

JAXB Error while using in SpringREST to return a ArrayList of a domain object

I am trying to use JAXB in Spring RESTful webservice.
My code is as follows:
#RequestMapping(value = "/countries",
method = RequestMethod.GET,
headers="Accept=application/xml, application/json")
public #ResponseBody CountryList getCountry() {
logger.debug("Provider has received request to get all persons");
// Call service here
CountryList result = new CountryList();
result.setData(countryService.getAll());
return result;
}
The CountryList.java class looks like:
#XmlRootElement(name="countries")
public class CountryList {
#XmlElement(required = true)
public List<Country> data;
#XmlElement(required = false)
public List<Country> getData() {
return data;
}
public void setData(List<Country> data) {
this.data = data;
}
}
The Country.java looks like:
#XmlRootElement(name="country")
public class Country {
private Calendar createdDt;
private String updatedBy;
private String createdBy;
private Long id;
private String countryName;
private Calendar updatedDt;
// getters and setters for all attributes goes here
}
Now, when I access the method getCountry(), I am getting the following exception
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
Class has two properties of the same name "data"
this problem is related to the following location:
at public java.util.List com.cisco.bic.services.model.CountryList.getData()
at com.cisco.bic.services.model.CountryList
this problem is related to the following location:
at public java.util.List com.cisco.bic.services.model.CountryList.data
at com.cisco.bic.services.model.CountryList
Would anyone has any idea why is this error coming. Am I doing anything wrong in the annotaion part ??
Please help.
Regards
Saroj
You can't annotate both the getter/setter and the field, you need to decide on one of them.

Resources