Broken Image when uploading using Rest Controller Multipart File - java

When i try to upload image in my RestController using MultipartFile, sometimes it creates a broken image (which doesn't open, just has some trashes inside file). It happens when i try to send (through Postman) images fast.
Here is my Controller:
#PostMapping("/upload/photo")
public ResponseEntity<ServerResponse> uploadPhoto(#RequestParam MultipartFile file, HttpServletRequest httpServletRequest) {
UserAccount userAccount = getPrincipal();
String localAddress = "http://" + getServerUrl(httpServletRequest);
ServerResponse response = userAccountService.addPhoto(userAccount, file, localAddress);
return getResponseEntity(response);
}
And my Service:
#Override
public ServerResponse<String> addPhoto(UserAccount userAccount, MultipartFile file, String localAddress) {
String uploadFilePath = uploadFile(file);
if(uploadFilePath.isEmpty()) {
return new ServerResponse<>(ResponseStatus.BAD_REQUEST, "Please select a file to upload", "");
}
final String PHOTO_URL = localAddress + "/" + uploadFilePath;
userAccount.setPhoto(PHOTO_URL);
userAccountRepository.save(userAccount);
return new ServerResponse<>(ResponseStatus.OK, null, PHOTO_URL);
}
private String uploadFile(MultipartFile file) {
if (file.isEmpty()) {
return "";
}
final String UPLOADED_FOLDER = "photos";
String uniqueName = generateRandomString();
String filePath = UPLOADED_FOLDER + "/" + uniqueName + file.getOriginalFilename();
new File(UPLOADED_FOLDER).mkdirs();
try {
byte[] bytes = file.getBytes();
Path path = Paths.get(filePath);
if (Files.exists(path)){
uniqueName = generateRandomString();
filePath = UPLOADED_FOLDER + "/" + uniqueName + file.getOriginalFilename();
path = Paths.get(filePath);
}
Files.write(path, bytes);
} catch (IOException e) {
e.printStackTrace();
}
return filePath;
}
I also tried to read multipart file as InputStream, but didin't help.
try (InputStream inputStream = file.getInputStream()) {
Files.copy(inputStream, path,
StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
e.printStackTrace();
}
I think the problem is, when i try to send couple images at the pretty same time it just can't handle that?

Related

Return file from Google Cloud Storage through REST API without downloading locally first

We would like to have a Java REST API to return files from Google Cloud Storage as attachment. I was able to able to get it to work using the following method. The problem is that the file has to be downloaded locally to the service container (we are deploying on Google Cloud Run) and this is a problem in the case of very large files, and may generally be bad practice. Is there a way to modify this code somehow to skip the creation of a local file?
#GetMapping(path = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<InputStreamResource> getSpecificFile(#RequestParam String fileName,
#RequestParam String bucketName, #RequestParam String projectName) {
Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
Blob blob = storage.get(bucketName, fileName);
ReadChannel readChannel = blob.reader();
String outputFileName = tempFileDestination.concat("\\").concat(fileName);
try (FileOutputStream fileOutputStream = new FileOutputStream(outputFileName)) {
fileOutputStream.getChannel().transferFrom(readChannel, 0, Long.MAX_VALUE);
String contentType = Files.probeContentType(Paths.get(outputFileName));
FileInputStream fileInputStream = new FileInputStream(outputFileName);
return ResponseEntity.ok().contentType(MediaType.valueOf(contentType))
.header("Content-Disposition", "attachment; filename=" + fileName)
.body(new InputStreamResource(fileInputStream));
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body(null);
} finally {
// delete the local file as cleanup
try {
Files.delete(Paths.get(outputFileName));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Well, that did not take me long to figure out. I was able to make it work as follows:
#GetMapping(path = "/file", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<InputStreamResource> getSpecificFile(#RequestParam String fileName, #RequestParam String bucketName, #RequestParam String projectName) {
Storage storage = StorageOptions.newBuilder().setProjectId(projectId).build().getService();
Blob blob = storage.get(bucketName, fileName);
ReadChannel readChannel = blob.reader();
try {
String contentType = Files.probeContentType(Paths.get(fileName));
InputStream inputStream = Channels.newInputStream(readChannel);
return ResponseEntity.ok().contentType(MediaType.valueOf(contentType))
.header("Content-Disposition", "attachment; filename=" + fileName)
.body(new InputStreamResource(inputStream));
} catch (IOException e) {
e.printStackTrace();
return ResponseEntity.internalServerError().body(null);
}
}
Basically redirect the InputStream to the readChannel instead of the file.

How to display uploaded images URL in browser

I am new for Multi-part in Spring Boot and I copied below code from internet for uploading files and its working fine. My requirement is after storing my files i just want to display them whenever I paste image URL in browser but using below code its getting download whenever I paste image URL.
How can I just show uploaded files instead of download?
controller
#RestController
public class FileController {
private static final Logger logger = LoggerFactory.getLogger(FileController.class);
#Autowired
private FileStorageService fileStorageService;
#PostMapping("/uploadFile")
public UploadFileResponse uploadFile(#RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/downloadFile/")
.path(fileName)
.toUriString();
return new UploadFileResponse(fileName, fileDownloadUri,
file.getContentType(), file.getSize());
}
#GetMapping("/downloadFile/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(#PathVariable String fileName, HttpServletRequest request) {
// Load file as Resource
Resource resource = fileStorageService.loadFileAsResource(fileName);
// Try to determine file's content type
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException ex) {
logger.info("Could not determine file type.");
}
// Fallback to the default content type if type could not be determined
if(contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
}
Service
#Service
public class FileStorageService {
private final Path fileStorageLocation;
#Autowired
public FileStorageService(FileStorageProperties fileStorageProperties) {
this.fileStorageLocation = Paths.get(fileStorageProperties.getUploadDir())
.toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
throw new FileStorageException("Could not create the directory where the uploaded files will be stored.", ex);
}
}
public String storeFile(MultipartFile file) {
// Normalize file name
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
// Check if the file's name contains invalid characters
if(fileName.contains("..")) {
throw new FileStorageException("Sorry! Filename contains invalid path sequence " + fileName);
}
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Could not store file " + fileName + ". Please try again!", ex);
}
}
public Resource loadFileAsResource(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if(resource.exists()) {
return resource;
} else {
throw new MyFileNotFoundException("File not found " + fileName);
}
} catch (MalformedURLException ex) {
throw new MyFileNotFoundException("File not found " + fileName, ex);
}
}
}
Remove the line:
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
The Content-Disposition HTTP header usually triggers the download behavior. So you don't want to use it.

Uploading an Image to a restful webservice produces an unviewable image

I'm using the following solution to try and receive an image in a restful webservice written in java:
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.TEXT_PLAIN)
public String getFile(#FormDataParam("pic") InputStream file,
#QueryParam("uid") String uid) {
try {
storeFile(file, uid);
} catch (IOException ex) {
Logger.getLogger(UploadImage.class.getName()).log(Level.SEVERE, null, ex);
return "failed";
}
return "success";
}
private void storeFile(InputStream input, String uid) throws IOException {
String absPath = PATH_TO_FILES + uid + ".jpg";
try {
OutputStream out = new FileOutputStream(new File(absPath));
int read = 0;
byte[] bytes = new byte[1024];
out = new FileOutputStream(new File(absPath));
while ((read = input.read(bytes)) != -1) {
out.write(bytes, 0, read);
}
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Here is the client code (javascript):
$scope.fileSelect = function (files) {
var file = files[0];
console.log("File loaded");
console.log(files);
console.log('uid = ' + $scope.uid + ' user = ' + $scope.user);
var formData = new FormData();
formData.append('pic', file);
var requestBody = {"token": $scope.token};
var req = {
method: 'POST',
url: 'http://192.168.0.9/resources/UploadPicture?uid=' + $scope.uid,
headers: {
'Content-Type': undefined
},
data: formData
};
console.log(FormData);
$http(req).then(function(response){
console.log(response);
}, function(error){
console.log(error);
});
};
This code produces a file that isnt viewable. The files im expecting are images.
So i got 2 questions:
Whenever the webservice gets called an a response is return, it seems like the image isnt fully flushed to the harddisk. After a while i can edit it. Is there a way to respond back to the client when the image is actually flushed to the disk?
How can i get the input stream to produce a viewable image when its written to the disk?
--edit--
After some fiddling with the file, i realized if i edit the image in notepad++ and took off the beggining and ending tags for the form boundaries, the image is viewable again:
Produced File
Is there a way for the form boundaries to stop interfering with the image data?
I found a solution using apache commons fileupload:
#POST
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.TEXT_PLAIN)
public String getFile(#Context HttpServletRequest request, #QueryParam("uid") String uid) {
ServletFileUpload upload = new ServletFileUpload();
try {
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField()) {
System.out.println("Form field " + name + " with value "
+ Streams.asString(stream) + " detected.");
} else {
System.out.println("File field " + name + " with file name "
+ item.getName() + " detected.");
// Process the input stream
storeFile(stream, uid);
}
}
return "success";
} catch (FileUploadException ex) {
Logger.getLogger(UploadImage.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(UploadImage.class.getName()).log(Level.SEVERE, null, ex);
}
return "failed.";
}

How to create MultipartFile with location file (path)

I have location file (C:\fakepath\Code.txt) . I want to create MultipartFile with this location. My Code:
public void fileUpload(String locationFile) {
Path path = Paths.get(locationFile);
String name = "Code.txt";
String originalFileName = "Code.txt";
String contentType = "text/plain";
byte[] content = null;
try {
content = Files.readAllBytes(path);
} catch (final IOException e) {
}
MultipartFile file = new MockMultipartFile(name, originalFileName, contentType, content);
try {
// Get the file and save it somewhere
byte[] bytes = file.getBytes();
Path paths = Paths.get(UPLOADED_FOLDER + file.getOriginalFilename());
Files.write(paths, bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
Also in this code I need type my file name it's not correct for my opinion. How to create MultipartFile and save somewhere? with location
In Windows you need double slashes "C://fakepath//Code.txt"

How to clean up temporary file after response in JAX-RS REST Service?

I am returning a temporary file from my JAX-RS REST Service like below:
#GET
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response getFile() {
File file = ... // create a temporary file
return Response.ok(file, MediaType.APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"" ) //optional
.build();
}
What is the correct way of removing this temporary file after the response has been processed? Is the JAX-RS implementation (like Jersey) supposed to do this automatically?
You can pass an instance of StreamingOutput that copies the content of the source file to the client output and eventually deletes the file.
final Path path = getTheFile().toPath();
final StreamingOutput output = o -> {
final long copied = Files.copy(path, o);
final boolean deleted = Files.deleteIfExists(path);
};
return Response.ok(output).build();
final File file = getTheFile();
return Response.ok((StreamingOutput) output -> {
final long copied = Files.copy(file.toPath(), output);
final boolean deleted = file.delete();
}).build();
The example on https://dzone.com/articles/jax-rs-streaming-response looks more helpful than the brief reply from Jin Kwon.
Here is an example:
public Response getDocumentForMachine(#PathParam("custno") String custno, #PathParam("machineno") String machineno,
#PathParam("documentno") String documentno, #QueryParam("language") #DefaultValue("de") String language)
throws Exception {
log.info(String.format("Get document. mnr=%s, docno=%s, lang=%s", machineno, documentno, language));
File file = new DocFileHelper(request).getDocumentForMachine(machineno, documentno, language);
if (file == null) {
log.error("File not found");
return Response .status(404)
.build();
}
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream out) throws IOException, WebApplicationException {
log.info("Stream file: " + file);
try (FileInputStream inp = new FileInputStream(file)) {
byte[] buff = new byte[1024];
int len = 0;
while ((len = inp.read(buff)) >= 0) {
out.write(buff, 0, len);
}
out.flush();
} catch (Exception e) {
log.log(Level.ERROR, "Stream file failed", e);
throw new IOException("Stream error: " + e.getMessage());
} finally {
log.info("Remove stream file: " + file);
file.delete();
}
}
};
return Response .ok(stream)
.build();
}

Categories