Spring WebFlux FilePart transferTo method can't write file to dest path - spring

import org.springframework.http.codec.multipart.FilePart;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;
import java.io.File;
#RestController
public class TestController {
#PostMapping("test")
public Mono<R> test(FilePart file){
String fileName = file.filename();
File newFile = new File("F:/images/banner/",fileName);
file.transferTo(newFile);
return Mono.just(R.ok());
}
}
i upload picture and use transferTo method to write file to the dest path.
but i can't found the picture in the F:/images/banner/
someone can help me? thanks

When writing reactive code you have to make sure it terminates, or in other words you need to subscribe to the publisher. The file.transferTo() returns a Mono<Void> which, if you don't subscribe, doesn't actually do anything.
What you can do in your case is the following, use the then method to return your result.
#PostMapping("test")
public Mono<R> test(FilePart file){
String fileName = file.filename();
File newFile = new File("F:/images/banner/",fileName);
return file.transferTo(newFile)
.then(Mono.just(R.ok()));
}
Now you return the call chain and the client will subscribe and things will start to happen. You could also add an additional onError call to return a error response when something breaks in the transfer.

Related

throw not found exception if pubsub topic is not available

I am using spring boot to interact with pubsub topic.
My config class for this connection look like this:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gcp.pubsub.core.PubSubTemplate;
import org.springframework.cloud.gcp.pubsub.core.publisher.PubSubPublisherTemplate;
import org.springframework.cloud.gcp.pubsub.support.PublisherFactory;
import org.springframework.cloud.gcp.pubsub.support.converter.SimplePubSubMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SettableListenableFuture;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
import com.google.pubsub.v1.PubsubMessage;
public abstract class PubSubPublisher {
private static final Logger LOGGER = LoggerFactory.getLogger(PubSubPublisher.class);
private final PubSubTemplate pubSubTemplate;
protected PubSubPublisher(PubSubTemplate pubSubTemplate) {
this.pubSubTemplate = pubSubTemplate;
}
protected abstract String topic(String topicName);
public ListenableFuture<String> publish(String topicName, String message) {
LOGGER.info("Publishing to topic [{}]. Message: [{}]", topicName, message);
return pubSubTemplate.publish(topicName, message);
}
}
And I am calling this at my service, like this:
publisher.publish(topic-name, payload);
This publish method is async one, which always pass on did not wait for acknowldgrment. I make add get after publish for wait until it get the response from pubsub.
But I wanted to know if in case my topic is not already present and i try to push some message, it should throw some error like resource not found, considering using default async method only.
Might be implementing the callback would help but i am unable to do that in my code. And the current override publish method which use callback is just throwing the WARN not exception i wanted that to be exception. that is the reason i wanted to implement the callback.
You can check if Topic already present
from google.cloud import pubsub_v1
project_id = "projectname"
topic_name = "unknowTopic"
publisher = pubsub_v1.PublisherClient()
topic_path = publisher.topic_path(project_id, topic_name)
try:
response = publisher.get_topic(topic_path)
except Exception as e:
print(e)
This returns the error as
404 Resource not found (resource=unknowTopic).

Spring returns EmptyCollectionEmbeddedWrapper instead of just an empty array

(Using only #RepositoryRestResource and methods defined inside the repository)When making an http request and a repository method returns no result, is there any workaround to change the returned "content" to an empty array if it really is empty , instead of :
HttpEntityMethodProcessor - Writing [Resources { content: [org.springframework.hateoas.core.EmbeddedWrappers$EmptyCollectionEmbeddedWrapp
My repo:
#RepositoryRestResource(collectionResourceRel = "some", path = "some")
public interface SomeRepository extends PagingAndSortingRepository<SomePojo, String>
{}
false:
"content": [
{
"value": [],
"rel": null,
"collectionValue": true,
"relTargetType": "xy.cxyPojo"
}
]
}
good:
"content": []
}
Edit: found the solution, it is the following:
The best and easiest solution i found finally for this so far is the following. Implement a custom ResourceProcessor, which is automatically picked up and used by spring(because of the #Component). Override the process method and in the method return a new Resource() which is initialized with an empty list, instead of the old Resource you got as an argument, add the links and all what you want and that's it. Like this:
import java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.ResourceProcessor;
import org.springframework.hateoas.Resources;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
#Component
public class ResourceProcessorEmpty implements ResourceProcessor<Resources<Object>>
{
#Override
public Resources<Object> process(final Resources<Object> resourceToThrowAway)
{
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
// In my case i needed the request link with parameters, and the empty content[] array
Link link = new Link(request.getRequestURL().toString() + "?" + request.getQueryString());
Resources<Object> newResource = new Resources<>(Collections.emptyList());
newResource.add(link);
return newResource;
}
}
For clarification: if you use Resources<Object>, that will handle empty collections(when that "EmptyCollectionEmbeddedWrapper" dummy object would be returned), whereas Resources<Resource<Object>> will handle non-empty collections. In this case the first needs to be used.

Spring Integration not moving file after processing

My requirement is to move files from input to output directory. Currently, I receive an XML file, parse it, process it and would like to move to new folder. I am using SPring boot 2.0, Spring INtegration 5. Attached is the code. This integration flows process the file but after processing it is not moving the file new directory.
Could you please let me know what is missing and how to fix this?
Logs are
2018-04-06 15:55:16.473[0;39m [32mDEBUG[0;39m [35m6364[0;39m [2m---[0;39m [2m[ask-scheduler-1][0;39m [36mo.s.i.handler.ServiceActivatingHandler [0;39m [2m:[0;39m handler 'ServiceActivator for [org.springframework.integration.handler.BeanNameMessageProcessor#33a55bd8] (org.springframework.integration.handler.ServiceActivatingHandler#0)' produced no reply for request Message: GenericMessage [payload=Producers {id: -2147483648, parent-id: 0}, headers={file_originalFile=C:\slim\OBDF\Entire_IMO_hierarchy.xml, id=3ee00fca-1f2b-be84-742a-b5c6edfaf42a, file_name=Entire_IMO_hierarchy.xml, file_relativePath=Entire_IMO_hierarchy.xml, timestamp=1523055316426}]
2018-04-06 15:55:16.475[0;39m [32mDEBUG[0;39m [35m6364[0;39m ---[0;39m [ask-scheduler-1][0;39m [36mo.s.integration.channel.DirectChannel [0;39m :[0;39m postSend (sent=true) on channel 'slimflow.channel#1', message: GenericMessage [payload=Producers {id: -2147483648, parent-id: 0}, headers={file_originalFile=C:\slim\OBDF\Entire_IMO_hierarchy.xml, id=3ee00fca-1f2b-be84-742a-b5c6edfaf42a, file_name=Entire_IMO_hierarchy.xml, file_relativePath=Entire_IMO_hierarchy.xml, timestamp=1523055316426}]
2018-04-06 15:55:16.480[0;39m [32mDEBUG[0;39m [35m6364[0;39m ---[0;39m [ask-scheduler-1][0;39m [36mo.s.integration.channel.DirectChannel [0;39m :[0;39m postSend (sent=true) on channel 'slimflow.channel#0', message: GenericMessage [payload=C:\slim\OBDF\Entire_IMO_hierarchy.xml, headers={file_originalFile=C:\slim\OBDF\Entire_IMO_hierarchy.xml, id=0f673954-bceb-6e64-0d47-639522002569, file_name=Entire_IMO_hierarchy.xml, file_relativePath=Entire_IMO_hierarchy.xml, timestamp=1523055316320}]
Integration flow config
import java.io.File;
import java.util.function.Function;
import javax.xml.bind.JAXBException;
import javax.xml.stream.XMLStreamException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.integration.dsl.Pollers;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.integration.file.FileWritingMessageHandler;
import org.springframework.integration.file.filters.AcceptOnceFileListFilter;
import org.springframework.integration.file.filters.ChainFileListFilter;
import org.springframework.integration.file.filters.RegexPatternFileListFilter;
import org.springframework.integration.transformer.PayloadTypeConvertingTransformer;
import org.springframework.messaging.MessageHandler;
#Configuration
#EnableIntegration
public class SlimIntegrationConfig {
#Value("${input.directory}")
private String inputDir;
#Value("${outputDir.directory}")
private String outputDir;
#Value("${input.scan.frequency: 100000}")
private long scanFrequency;
#Autowired
private XmlBeanExtractor<Producers> xmlBeanExtractor;
#Bean
public MessageSource<File> inputFileSource() {
FileReadingMessageSource src = new FileReadingMessageSource(
(f1, f2) -> Long.valueOf(f1.lastModified()).compareTo(f2.lastModified()));
src.setDirectory(new File(inputDir));
src.setAutoCreateDirectory(true);
ChainFileListFilter<File> chainFileListFilter = new ChainFileListFilter<>();
chainFileListFilter.addFilter(new AcceptOnceFileListFilter<>() );
chainFileListFilter.addFilter(new RegexPatternFileListFilter("(?i)^.+\\.xml$"));
src.setFilter(chainFileListFilter);
return src;
}
#Bean
public DirectChannel outputChannel() {
return new DirectChannel();
}
#Bean
public MessageHandler fileOutboundChannelAdapter() {
FileWritingMessageHandler adapter = new FileWritingMessageHandler(new File(outputDir));
adapter.setDeleteSourceFiles(true);
adapter.setAutoCreateDirectory(true);
adapter.setExpectReply(false);
return adapter;
}
#Bean
PayloadTypeConvertingTransformer<File, Producers> xmlBeanTranformer() {
PayloadTypeConvertingTransformer<File, Producers> tranformer = new PayloadTypeConvertingTransformer<>();
tranformer.setConverter(file -> {
Producers p = null;
try {
p = xmlBeanExtractor.extract(file.getAbsolutePath(), Producers.class);
} catch (JAXBException | XMLStreamException e) {
e.printStackTrace();
}
return p;
});
return tranformer;
}
#Bean
public IntegrationFlow slimflow() {
return IntegrationFlows
.from(inputFileSource(), spec -> spec.poller(Pollers.fixedDelay(scanFrequency)))
.transform(xmlBeanTranformer())
.handle("slimFileProcessor","processfile")
.channel(outputChannel())
.handle(fileOutboundChannelAdapter())
.get()
;
}
}
We need top know what your slimFileProcessor.processfile() does. However it doesn't reflect what you do in the xmlBeanTranformer. You convert there a File payload to the Producers object and exactly this one is sent to the slimFileProcessor.
So, that's first: there is no File in the payload for the FileWritingMessageHandler. But we can fix it a bit later.
Now you have a log like:
ServiceActivatingHandler#0)' produced no reply for request
So, your slimFileProcessor doesn't return something to be sent to the outputChannel() for potential file move from one directory to another.
If return something isn't possible by the logic at all, you can consider to use a .publishSubscribeChannel(). Make that xmlBeanTranformer() as a one subscriber and fileOutboundChannelAdapter() as another. This way the same File object will be sent to two branches. Only the point that the second branch won't be called until the first one finishes its work. Of course, if everything is done in the same thread.
You still can live with a simple linear flow, just because you get a gain of the FileHeaders.ORIGINAL_FILE header which is going to be used in the FileWritingMessageHandler. But you should keep in mind that last one supports only these types for request message payload: File, InputStream, byte[] or String. For your move after process use-case, of course, it would be better to deal with the File type. That's why I suggest to consider publish-subscribe variant.

Spring UTF-8 replace chars "ě", "č", "ř" to "?"

I have problems with UTF-8 encoding.
I have a Spring-Boot REST Server. When I perform a PUT request to save an article and there are special characters in the content (like "ě", "č" or "ř") they will be replace by "?" characters. But if I edit an article using phpmyadmin and perform a GET request it returns "ě", "č" and "ř" correctly.
package cz.flay.fellcms.http;
import cz.flay.fellcms.dao.ArticlesRepository;
import cz.flay.fellcms.entities.Article;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
#CrossOrigin
#RestController
#RequestMapping(path = "/api/articles", produces = "application/json;charset=UTF-8")
public class ArticleRestController {
#Autowired
private ArticlesRepository articlesRepository;
#CrossOrigin
#GetMapping(path = "/all")
public #ResponseBody Iterable<Article> getAll(){
return articlesRepository.getAll();
}
#CrossOrigin
#GetMapping(path = "/newest")
public #ResponseBody Iterable<Article> getNewest(){
return articlesRepository.getNewest();
}
#CrossOrigin
#PutMapping(path = "/save")
public #ResponseBody HttpStatus saveArticle(#RequestBody Article article) {
articlesRepository.save(article);
return HttpStatus.OK;
}
#CrossOrigin
#GetMapping(path = "/get")
public #ResponseBody Article getArticle(#RequestParam int id) {
return articlesRepository.findOne(id);
}
}
I tried to log an article out to the console. Then the special charactes are displayed correctly.
I tried a lot of things that I found in other discussions but nothing helped me.
Thanks for any help !
The way it worked for me was to enter the following in the mysql configuration file (my.cnf)
[mysql]
default-character-set=utf8
[mysqld]
character-set-server=utf8
The mysql configuration file is usually in /etc/mysql/my.cnf in linux (I tried it in Ubuntu 14.04). If it doesn't exist there you can create the file using sudo touch /etc/mysql/my.cnf; then edit the file.
Edit:
For Win Users: File is located in /mysql/bin/my.ini
Try this in your properties file;
spring.datasource.url = jdbc:mysql://localhost:3306/db_name?useUnicode=yes&characterEncoding=UTF-8

I update avatar image and display it but the avatar does not change in Spring Boot , why?

I use Spring boot to develop a website.I develop a function of uploading avatar image , when I update avatar image and display it but the avatar does not change and the image already has changed in the folder, why?
The problem is http cache?
my simple project:
dropbox link to my project
(note: you need to change the local path in TestingController)
You are not able to see the uploaded picture instantly because you save the image in the static files of your application. You are able to see the the saved image in your folder but not if you call your url. If you refresh your running project after uploading the file (project root and hit F5) you should be able to see it.
But the better solution would be that you have a #RequestMapping to load the picture and show it in browser.
Build on your project, try to use it like this:
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
import org.json.JSONObject;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
#Controller
public class TestingController {
private final Path p = Paths.get("/Users/devin/Documents/workspace-sts-3.8.1.RELEASE/testing/src/main/resources/static/uploads/images");
#RequestMapping(value={ "/", "/home"})
public String home(){
return "home";
}
#RequestMapping(value = "/post_upload_avatar_file", method = RequestMethod.POST)
#ResponseBody
public Object uploadAvatarFile(#RequestParam("uploadfile") MultipartFile uploadfile) {
JSONObject resJsonData=new JSONObject();
try {
if(uploadfile.isEmpty()){
System.out.println("Empty");
}
Files.copy(uploadfile.getInputStream(), p.resolve(uploadfile.getOriginalFilename()));
resJsonData.put("status", 200);
resJsonData.put("message", "Success!");
resJsonData.put("data", uploadfile.getOriginalFilename());
}catch (Exception e) {
System.out.println(e.getMessage());
resJsonData.put("status", 400);
resJsonData.put("message", "Upload Image Error!");
resJsonData.put("data", "");
}
return resJsonData.toString();
}
#GetMapping("files/{filename:.+}")
#ResponseBody
public ResponseEntity<Resource> serverFile(#PathVariable String filename){
Resource file = loadAsResource(filename);
return ResponseEntity
.ok()
.body(file);
}
public Resource loadAsResource(String filename) {
try {
Path file = p.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if(resource.exists() || resource.isReadable()) {
return resource;
}
else {
System.out.println("no file");
}
} catch (MalformedURLException e) {
System.out.println(e);
}
return null;
}
public Stream<Path> loadAll() {
try {
return Files.walk(p, 1)
.filter(path -> !path.equals(p))
.map(path -> p.relativize(path));
} catch (IOException e) {
System.out.println(e);
}
return null;
}
}
In this code I dont implement exception and error handling.
You can upload your picture like it was. And then you can call another url to receive the picture in your browser.
http://localhost:8080/files/testing.png
And the picture should be the response.
Please have a look to those two sources for a complete solution.
Spring file upload - backend
Spring file upload - frontend

Resources