According to the tickets at Is it possible to send binary data with STOMP over WebSockets using Spring-WebSockets? 's answer it should be possible to send and receive files using spring-websockets.
Currently I'm converting the file to an ArrayBuffer and then sending it:
fileInput.addEventListener('change', function(e) {
var file = fileInput.files[0];
var reader = new FileReader();
reader.readAsArrayBuffer(file);
stompClient.send("/app/requests." + channelId + ".files", {}, reader.result);
});
However as far as I can tell the controller doesn't receive the Bytearray in the messages payload.
#MessageMapping("requests.{channelId}.files")
public void receiveFile(#DestinationVariable String channelId, Message<?> message) throws Exception {
...
}
Could anyone please post a working example on how this should be done properly?
Thank you
Related
message send to the servicebus can be processed by consumer after some time, simply by setting ScheduledEnqueueTime param.
Its easy to achieve this while working on "raw" service bus message ie:
var serviceBusMessage = new ServiceBusMessage(json);
serviceBusMessage.setScheduledEnqueueTime(someTime);
but I have azure functions java app, that has:
#FunctionName("Process-Notifications")
public void processNotifications(
#ServiceBusQueueTrigger(name = "MessageCmd", queueName = "queue_name_notify_cmd_v1", connection = "SBusConn") String messageCmd,
#ServiceBusQueueOutput(name = "NotifyForRetry", queueName = "queue_name_notify_cmd__v1", connection = "SBusConn") OutputBinding<String> notifyForRetry,
sending notification is easy here, I only do:
Map<String, Object> result = mapper.convertValue(json, new TypeReference<>() {
});
String channels = "";
if (error instanceof SmsOnlyError) {
channels+="sms";
}
if (error instanceof EmailOnlyError) {
channels+="email";
}
if (error instanceof AllDefinedChannelsError) {
channels+="sms, email";
}
properties.put("retry_channels", channels);
result.put("UserProperties", properties);
result.put("ScheduledEnqueueTime", OffsetDateTime.now(defaultClock).plusMinutes(15).toString());
JsonNode newJson = mapper.convertValue(result, JsonNode.class);
hard to find something in docs, maybe someone here can me ?
thanks!
and works fine. But I have no idea how to set ScheduledEnqueueTime there..
Unfortunately, this isn't currently supported by non-C# languages in Azure Functions. You would have to use the Azure SDK for Service Bus directly in your code.
This is the azure web page example in JAVA to get the message content from the azure service bus :
#FunctionName("sbprocessor")
public void serviceBusProcess(
#ServiceBusQueueTrigger(name = "msg",
queueName = "myqueuename",
connection = "myconnvarname") String message,
final ExecutionContext context
) {
context.getLogger().info(message);
}
This only return the content of the message. How is it possible to get the other fields that you can see in Service bus explorer : Label, Custom Properties and Broker Properties ?
You can retrieve message metadata by adding #BindingName("UserProperties") etc. annotation to method parameters like below for example. You can bind to any metadata of a message using binding expression. In this case below, it's "Properties" and "Label".
#FunctionName("sbprocessor")
public void serviceBusProcess(
#ServiceBusQueueTrigger(name = "msg", queueName = "myqueuename", connection = "myconnvarname")
String message,
final ExecutionContext context,
#BindingName("UserProperties")
Map<String, Object> properties,
#BindingName("Label")
String label) {
context.getLogger().info("Message received: " + message + " , properties: " + properties + " , label: " + label);
}
I used Service Bus Explorer as Message Sender to set metadata of the message as below and was able to see those in the consumer side using above code in "UserProperties" binding.
N.B. C# function SDK has a benefit here over Java. In C#, you can get the whole BrokeredMessage object which is easier to navigate for metadata directly. But unfortunately, that's not possible in Java SDK as of now where you have to bind separately.
In my code currently, I get data from the database and then I write a file out of the data. I have this kind of camel route and working solution:-
private static final String INPUT_FILE_DIRECTORY_URI = "file:" + System.getProperty("user.home")
+ "/data/cdr/?noop=false";
private static final String SFTP_SERVER = "sftp://" +System.getProperty("user.name")
+ "#sftp_server_url/data/cdr/?privateKeyFile=~/.ssh/id_rsa&passiveMode=true";
from(INPUT_FILE_DIRECTORY_URI)
.streamCaching()
.log("Sending file to local sftp")
.to(SFTP_SERVER);
I don't want to write a file in the local disk. Instead, I want to write file data directly to the SFTP server. I don't know how to do it? But I imagine it should be possible to do it. Can you tell me is it possible? If yes, how to do it?
I managed to solve this problem in another way. It is more suitable for my particular problem.
byte[] csvData = csvStringBuilder.toString().getBytes();
Routes.withProducer(producer)
.withHeader(Exchange.FILE_NAME, myCsvFile.csv)
.withBody(csvData)
.to(SFTP_SERVER).request(byte[].class);
You shouldn't use streamCaching unless you really using it. It store your file in memory, use it if you need to consume multiples times your input.
You can use Jpa component or a custom bean getting your data. Load it from database and then send it to your ftp server.
With Jpa :
#Entity
#NamedQuery(name = "data", query = "select x from Data x where x.id = 1")
public class Data { ... }
After that you can define a consumer uri like this one:
from("jpa://org.examples.Data?consumer.namedQuery=data")
.to("SFTP_SERVER");
EDIT : to convert a list to csv and send it to ftp :
from("jpa://org.examples.Data?consumer.namedQuery=data")
.marshal()
.csv()
.to("sftp://" +System.getProperty("user.name") +
"#sftp_server_url/data/cdr/myFile.csv?" +"privateKeyFile=~/.ssh/id_rsa&passiveMode=true");
See CSV component who convert a list to a csv file.
Yes it is possible :) To do this send the file inputStream in a camel DIRECT component and in the associated route make the copy to FTP. I use this case, to upload a file and directly copy it to ftp with from(directInputStreamName).to(yourFtpUri). This is an sample code :
Your service
#Service
public class FileService {
#Produce(uri = PfnumDownloadConstants.CAMEL_DIRECT_UPLOAD)
private ProducerTemplate producer;
public void sendFileToFtp(File fileToSend, String ftpDestinationUri) throws IOException {
Map<String, Object> headers = new HashMap<>();
//In this variable you can init the ftp destination uri or you can hard code it in route
headers.put("destinationUri", ftpDestinationUri);
//set filename to name your file in ftp
headers.put(Exchange.FILE_NAME_ONLY, file.getName());
InputStream targetStream = new FileInputStream(file);
//send stream as body and list of headers to direct
producer.sendBodyAndHeaders(targetStream, headers);
}
}
Your Camel route
#Component
public class FileUploadRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
//Manage camel exception in a dedicated processor
onException(Exception.class).process(exceptionProcessor).log("error :: ${exception}");
from(CAMEL_DIRECT_UPLOAD)
.log("file copy to ftp '${header.CamelFileNameOnly}' in process")
.toD("file:/mnt?fileName=${header.CamelFileNameOnly}&delete=false")
.log("copy done");
}
}
Created a bot and group. Added bot to the group(as admin) and starting trying posts. Through Url, it's smooth and successful. Started off exploring Telegram API(JAVA). Tests were on getUpdate and sendMessage methods.
I have generated TOKEN from telegram webapp(https://web.telegram.org).
Code snippet to getUpdates: yes, I have included bot ahead of the token.
TelegramBot bot = new TelegramBot("BOT_TOKEN");
GetUpdates getUpdates = new GetUpdates().limit(100).offset(0).timeout(0);
GetUpdatesResponse gur = bot.execute(getUpdates);
List<Update> list = gur.updates();
for(Update update : list) {
System.out.println(update.message());
}
Resonse is null.
code for sendMessage:
SendMessage request = new SendMessage(chatId, text)
.parseMode(ParseMode.HTML)
.disableWebPagePreview(true)
.disableNotification(true)
.replyToMessageId(1)
.replyMarkup(new ForceReply());
// sync
SendResponse sendResponse = bot.execute(request);
boolean ok = sendResponse.isOk();
Message message = sendResponse.message();
System.out.println(ok);
System.out.println(message);
Response is false and null.
I'm referring https://github.com/pengrad/java-telegram-bot-api#send-message
Help me understand the mistake. Thanks.
You need 109780439:AAJqs_w-4 format token, for instance:
TelegramBot bot = new TelegramBot("109780439:AAJqs_w-4");
This token can be obtained from #BotFather.
I am using a document converter api called cloudconvert. They don't have an official java library, but a third party java option. I needed a little customization so I cloned the github project and added it to my project. I am sending cloudconvert a .epub file and getting a .pdf file in return. If I use the default settings it works without issue and properly converts my .epub to a .pdf. Here is the code that makes it happen.
Here is what triggers the conversion:
// Create service object
CloudConvertService service = new CloudConvertService("api-key");
// Create conversion process
ConvertProcess process = service.startProcess(convertFrom, convertTo);
// Perform conversion
//convertFromFile is a File object with a .epub extension
process.startConversion(convertFromFile);
// Wait for result
ProcessStatus status;
waitLoop:
while (true) {
status = process.getStatus();
switch (status.step) {
case FINISHED:
break waitLoop;
case ERROR:
throw new RuntimeException(status.message);
}
// Be gentle
Thread.sleep(200);
}
//Download result
service.download(status.output.url, convertToFile);
//lean up
process.delete();
startConversion() calls:
public void startConversion(File file) throws ParseException, FileNotFoundException, IOException {
if (!file.exists()) {
throw new FileNotFoundException("File not found: " + file);
}
startConversion(new FileDataBodyPart("file", file));
}
Which calls this to actually send the POST request using jersey:
private void startConversion(BodyPart bodyPart) {
if (args == null) {
throw new IllegalStateException("No conversion arguments set.");
}
MultiPart multipart = new FormDataMultiPart()
.field("input", "upload")
.field("outputformat", args.outputformat)
.bodyPart(bodyPart);
//root is a class level WebTarget object
root.request(MediaType.APPLICATION_JSON).post(Entity.entity(multipart, multipart.getMediaType()));
}
Up to this point everything is working. My problem is that the when the conversion happens the .pdf that returns has very small margins. cloudconvert provides a way to change those margins. You can send in an optional json param converteroptions and set the margins manually. I have tested this out using postman and it works without issue, I was able to get a properly formatted margin document. So know this is possible. Here is the POSTMAN info I used:
#POST : https://host123d1qo.cloudconvert.com/process/WDK9Yq0z1xso6ETgvpVQ
Headers: 'Content-Type' : 'application/json'
Body:
{
"input": "base64",
"file": "0AwAAIhMAAAAA", //base64 file string that is much longer than this
"outputformat": "pdf",
"converteroptions": {
"margin_bottom": 75,
"margin_top": 75,
"margin_right": 50,
"margin_left": 50
}
}
Here are my attempts at getting the POST request formatted properly, I'm just not very experienced with jersey and the couple of answers I did find on stackoverflow didn't work for me.
Attempt 1, I tried adding the json string as a Multipart.field. It didn't give me any errors and still returned a converted .pdf file, but the margins didn't get changed so I must not be sending it back right.
private void startConversion(BodyPart bodyPart) {
String jsonString = "{\"margin_bottom\":75,\"margin_top\":75,\"margin_right\":50,\"margin_left\":50}";
MultiPart multipart = new FormDataMultiPart()
.field("input", "upload")
.field("outputformat", args.outputformat)
.field("converteroptions", jsonString)
.bodyPart(bodyPart);
root.request(MediaType.APPLICATION_JSON).post(Entity.entity(multipart, multipart.getMediaType()));
}
Attempt 2, when I had it working in POSTMAN it was using the 'input' type as 'base64' so I tried changing it to that but it this time it doesn't return anything at all, no request errors, just a timeout error at the 5 minute mark.
//I pass in a File object rather than the bodypart object.
private void startConversion(File file) {
byte[] encoded1 = Base64.getEncoder().encode(FileUtils.readFileToByteArray(file));
String encoded64 = new String(encoded1, StandardCharsets.US_ASCII);
String jsonString = "{\"margin_bottom\":75,\"margin_top\":75,\"margin_right\":50,\"margin_left\":50}";
MultiPart multipart = new FormDataMultiPart()
.field("input", "base64")
.field("outputformat", args.outputformat)
.field("file", encoded64)
.field("converteroptions", jsonString);
root.request(MediaType.APPLICATION_JSON).post(Entity.entity(multipart, multipart.getMediaType()));
}
Attempt 3, after some googling on how to properly send jersey json post requests I changed the format. This time it returned a 400 bad request error.
private void startConversionPDF(File file) throws IOException {
byte[] encoded1 = Base64.getEncoder().encode(FileUtils.readFileToByteArray(file));
String encoded64 = new String(encoded1, StandardCharsets.US_ASCII);
String jsonString = "{\"input\":\"base64\",\"file\":\"" + encoded64 + "\",\"outputformat\":\"pdf\",\"converteroptions\":{\"margin_bottom\":75,\"margin_top\":75,\"margin_right\":50,\"margin_left\":50}}";
root.request(MediaType.APPLICATION_JSON).post(Entity.json(jsonString));
}
Attempt 4, Someone said you don't need to manually use a jsonString you should use serializable java beans. So I created the corresponding classes and made the request like shown below. Same 400 bad request error.
#XmlRootElement
public class PDFConvert implements Serializable {
private String input;
private String file;
private String outputformat;
private ConverterOptions converteroptions;
//with the a default constructor and getters/setters for all
}
#XmlRootElement
public class ConverterOptions implements Serializable {
private int margin_bottom;
private int margin_top;
private int margin_left;
private int margin_right;
//with the a default constructor and getters/setters for all
}
private void startConversionPDF(File file) throws IOException {
byte[] encoded1 = Base64.getEncoder().encode(FileUtils.readFileToByteArray(file));
String encoded64 = new String(encoded1, StandardCharsets.US_ASCII);
PDFConvert data = new PDFConvert();
data.setInput("base64");
data.setFile(encoded64);
data.setOutputformat("pdf");
ConverterOptions converteroptions = new ConverterOptions();
converteroptions.setMargin_top(75);
converteroptions.setMargin_bottom(75);
converteroptions.setMargin_left(50);
converteroptions.setMargin_right(50);
data.setConverteroptions(converteroptions);
root.request(MediaType.APPLICATION_JSON).post(Entity.json(data));
}
I know this is quite the wall of text, but I wanted to show all the different things I tried so that I wouldn't waste anyone's time. Thank you for any help or ideas you might have to make this work. I really want to make it work with jersey because I have several other conversions I do that work perfectly, they just don't need any converteroptions. Also I know its possible because it works when manually running the process through POSTMAN.
Cloudconvert api documentation for starting a conversion
Github repo with the recommended 3rd party java library I am using/modifying
I finally figured it out. Hours of trial and error. Here is the code that did it:
private void startConversionPDF(File file) throws IOException {
if (args == null) {
throw new IllegalStateException("No conversion arguments set.");
}
PDFConvert data = new PDFConvert();
data.setInput("upload");
data.setOutputformat("pdf");
ConverterOptions converteroptions = new ConverterOptions();
converteroptions.setMargin_top(60);
converteroptions.setMargin_bottom(60);
converteroptions.setMargin_left(30);
converteroptions.setMargin_right(30);
data.setConverteroptions(converteroptions);
MultiPart multipart = new FormDataMultiPart()
.bodyPart(new FormDataBodyPart("json", data, MediaType.APPLICATION_JSON_TYPE))
.bodyPart(new FileDataBodyPart("file", file));
root.request(MediaType.APPLICATION_JSON).post(Entity.entity(multipart, multipart.getMediaType()));
}