How do I get EventSource.onmessage to work?
Here is my subscribe and pushevent code:
public SseEmitter subscribe() throws Exception {
SseEmitter emitter = new SseEmitter(1800000L);
emitters.add(emitter);
emitter.onCompletion(() -> {
synchronized (emitters) {
emitters.remove(emitter);
}
});
emitter.onTimeout(() -> {
emitter.complete();
emitters.remove(emitter);
});
return emitter;
}
#Async
public void pushEventMap(Map<String, Object> pushMap) throws IOException {
List<SseEmitter> deadEmitters = new ArrayList<>();
HashMap<String,Object> map = (HashMap<String,Object>) pushMap;
emitters.forEach(emitter -> {
try {
emitter.send(SseEmitter.event().name("myEvent").data(map));
} catch (Exception e) {
emitter.completeWithError(e);
logger_error.error("pushEvent Exception:" + e);
deadEmitters.add(emitter);
}
});
emitters.removeAll(deadEmitters);
}
The controller for the above service is:
#RequestMapping(value = "/subscribe", produces = "text/event-stream")
public ResponseEntity<SseEmitter> subscribe() throws Exception {
final SseEmitter emitter = eventService.subscribe();
return new ResponseEntity<>(emitter, HttpStatus.OK);
}
#RequestMapping(value = "/publish")
public void publish() throws IOException {
eventService.pushEventMap(pushMap);
}
I want the client to receive the data of event push through js.
const eventInit = () => {
console.log("eventInit called");
const url = 'http://localhost:8080/itf/subscribe';
const eventSource = new EventSource(url);
var httpRequest = new XMLHttpRequest();
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('===');
console.log(data);
}
eventSource.onopen = (event) => {
console.log('sse open');
console.log(event);
}
eventSource.onerror = (event) => {
if (event.readyState == EventSource.CLOSED) {
console.log('sse close');
} else {
console.log("onerror", e);
}
}
}
In this state, if I send event-generating data through postman, sse open appears on the console.
However, the result of the event is not displayed.
If I directly access /itf/subscribe through the url, the result of the event is displayed on the screen. However, I am not receiving the result of the event through event.onmessage.
What I want is to raise an event, and then receive the result of the event through the onmessage function.
I am wondering how can I get the result of the event.
best regards
There is no issue with code, the actual issue is when I am writing response to client my response message should look like as below.
PrintWriter out = response.write("data: message"+value+"\n\n");
out.flush(); //don't forget to flush
In my code I was missing the last part "\n\n" in response object so source.onmessage(datalist) in javascript didn't get hit.
eventSource.addEventListener('myEvent', function (event) {
console.log(event);
const data = JSON.parse(event.data);
console.log('addEventListener');
console.log(data);
// console.log(data.siteCode);
eventEffect(data);
}, false);
this is normally work!
Related
I have a problem with sharing image between Spring and React. What i do:
I get file from input:
<input type='file' id='upload-button' accept='image/*'
onBlur={() => image.onBlur()}
onChange={e => onChangeHandler(e)}/>
Then my handlerMethod with base64Encoder:
const onChangeImage = async (e: any) => {
const file = e.target.files[0]
const base64 = await convertToBase64(file)
setValue(base64)
}
export const convertToBase64 = (file: any) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.onload = () => {
resolve(fileReader.result)
}
fileReader.onerror = (error) => {
reject(error)
}
})
}
And after that i send this file to method:
DishesService.addDish(dish, image.value)
This method:
export default class DishesService {
static async addDish(dish: IDish, file: any) {
try {
await axios.post<IDish>('http://localhost:8080/dishes', dish)
.then(response => {
this.updateDishImage(response.data.id, file)
})
} catch (e) {
console.log('произошла ошибка при добавлении блюда')
}
}
static async updateDishImage(id: number | undefined, image: any) {
try {
await axios.put('http://localhost:8080/dishes/' + id, {}, {
params: {
file: image
}
})
} catch (e) {
console.log('Произошла ошибка при добавлении картинки к блюду')
}
}
}
And my Spring Boot controller:
#PutMapping(path = "{dishId}")
public ResponseEntity<DishEntity> updateDishImage(#PathVariable Long dishId, #RequestParam("file") String base64File) {
DishEntity updateDish = dishService.updateDishImage(base64File, dishId);
return ResponseEntity.ok(updateDish);
}
Method:
#Override
public DishEntity updateDishImage(String base64File, Long id) {
DishEntity dishById = findById(id);
byte[] byteImage = Base64.decodeBase64(base64File);
dishById.setImage(byteImage);
DishEntity updatedDish;
try {
updatedDish = dishRepository.save(dishById);
} catch (Exception ex) {
throw new OperationFailedException("Update dish image method failed!");
}
return updatedDish;
}
Whan i do my code, I get exception:
Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible value. Expected the discarded bits to be zero.
If you faced with this problem, please, help me to fix this error
Front: Angular2+
Back: Java15 Spring Boot
DataBase: MySql
I take all examples for upload files in database form in byte[]. with a complex object with file by attribute.
I try desperately to upload an image file on my database so I try to create post API rest by I have error
I'm take
o.s.web.servlet.PageNotFound : Request method 'POST' not supported
#PostMapping(value = "/Ninja/image/")
public ResponseEntity<ResponseMessage> saveNinjaImage(#RequestParam("file") MultipartFile file) {
String message = "";
try {
Iterable<Syndic> lstN= NinjaRepository.findAll();
if(lstN.iterator().hasNext()) {
Ninja s = lstN.iterator().next();
s.setPicture(file.getBytes());
logger.info(s.toString());
}
} catch(Exception e) {
//return ResponseEntity.status(HttpStatus.EXPECTATION_FAILED).body(new ResponseMessage(message));
logger.info(e.toString());
}
return ResponseEntity.status(HttpStatus.OK).body(new ResponseMessage(message));
}
//service
public postNinja(file: File) {
const formData: FormData = new FormData();
formData.append('file', file);
const req = new HttpRequest('POST', `${this.host}/Ninja/image`, formData, {
headers: this.headers,
reportProgress: true,
responseType: 'json'
});
return this.http.request(req);
}
/* ts */
public onSave() {
this.frontDynamic.submitted = true;
if (this.frontDynamic.updateNinja.invalid) {
return;
} else {
const formValue = this.frontDynamic.updateNinja.value;
const newNinja = new Ninja(formValue['nom']);
newNinja.address = formValue['adresse'];
newNinja.description = formValue['description'];
newNinja.website = formValue['site'];
this.ninjaService.putNinja(newNinja,formValue['image']).subscribe(response => {},
error => {console.log(error);}
,
()=>{
this.ninjaService.postNinja(formValue['image']).subscribe(
event => {},
err => {});
);
}
}
The error happened when i upload a 115KB image file to server.(the most answer of stackoverflow is about download the file.I do not know if it is the same to those)
the error information is below:
onFailure : java.net.ProtocolException: unexpected end of stream
Relevant Code:
public void upLoadImageFile(String uploadUrl, File file, Map<String, String> maps, final HWUploadListener listener) {
final CallbackHandler handler = new CallbackHandler(listener);
try {
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
if (maps == null) {
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\";filename=\"file.jpg\""),
RequestBody.create(MediaType.parse("image/jpeg"), file)).build();
} else {
for (String key : maps.keySet()) {
builder.addFormDataPart(key, maps.get(key));
}
builder.addPart(Headers.of("Content-Disposition", "form-data; name=\"image\";filename=" + file.getName()), RequestBody.create(MediaType.parse("image/jpeg"), file)
);
}
RequestBody body = builder.build();
final Request request = new Request.Builder().url(uploadUrl).post(body).build();
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
call.enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
UtilUtils1.log("HuowuSdk", "onFailure :" + e.toString());
handler.uploadFailure(e.toString());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
String result = response.body().string();
handler.uploadSuccess(result);
} else {
handler.uploadFailure(response.message());
}
}
});
} catch (Exception e) {
UtilUtils1.log("HuowuSdk", e.toString());
handler.uploadError(e.toString());
}
}
Appreciate your answer!!
Here in this line below you have to increase write timeout because while uploading your write timeout expires that may be the reason so in below line increase writeTimeout limit:
final Call call = mOkHttpClient.newBuilder().writeTimeout(50, TimeUnit.SECONDS).build().newCall(request);
The following is my cloud code to send parse push notification:
Parse.Cloud.define('push1', function(response)
{
var query = new Parse.Query(Parse.Installation);
Parse.Push.send
({
where: query,
data:
{
alert: "Hai everyone..!!"
}
},
{
useMasterKey : true,
success: function()
{
response.success("true");
},
error: function(error)
{
response.error("error");
}
});
});
But the device from which I call this function only receives the notification. No other devices receives it. There are 3 devices added to the Installation class.
this is the app code from which I call this function
HashMap<String, Object> params = new HashMap<String, Object>();
ParseCloud.callFunctionInBackground("push1", params, new FunctionCallback<String>()
{
#Override
public void done(String ratings, ParseException e)
{
if (e == null)
{
System.out.println("WORKED: " + ratings);
}
else
System.out.println(e.toString());
}
});
I'm building a system which has push notification feature and use Jersey to create API.
I read an article about comet approach and end up with the following code:
Index.js
function checkExamNotification() {
$.ajax({
url: contextPath + '/api/notification/checkExamNotification',
type: 'get',
data: {
accountId: accountId,
sessionId: sessionId
},
success: function (res) {
console.log("success");
displayNumberOfNotification();
checkExamNotification();
},
error: function (jqXHR, textStatus, errorThrown) {
if (textStatus === "timeout") {
checkExamNotification();
}
}
});
}
$(document).ready(function () {
$.ajaxSetup({
timeout: 1000*60*3
});
checkExamNotification();
});
Check exam notification API
#GET
#Path("/checkExamNotification")
public Response checkExamNotification(#QueryParam("accountId") int accountId, #QueryParam("sessionId") String sessionId) throws InterruptedException {
if (memCachedClient.checkSession(sessionId, accountId)) {
while (!examNotificationQueue.hasItems()) {
Thread.sleep(5000);
}
ExamNotificationQueueItemModel examNotificationQueueItemModel = examNotificationQueue.dequeue();
if (examNotificationQueueItemModel.getAccountId() == accountId) {
LOGGER.info("[START] Check exam notification API");
LOGGER.info("Account ID: " + accountId);
LOGGER.info("Get notification with exam ID: " + examNotificationQueueItemModel.getExamId());
ExamEntity exam = examDAO.findById(examNotificationQueueItemModel.getExamId());
NotificationEntity notification = notificationDAO.findByExamId(exam.getExamid());
notification.setSend(1);
notificationDAO.getEntityManager().getTransaction().begin();
notificationDAO.update(notification);
notificationDAO.getEntityManager().getTransaction().commit();
LOGGER.info("[END]");
String result = gson.toJson(examNotificationQueueItemModel);
return Response.status(200).entity(result).build();
} else {
examNotificationQueue.enqueue(examNotificationQueueItemModel);
Thread.sleep(5000);
checkExamNotification(accountId, sessionId);
}
}
return Response.status(200).entity(gson.toJson("timeout")).build();
}
From my debug, the API did finish return but the success event SOMETIMES didn't fire.
Yes, sometimes console log success but sometimes it doesn't.
Can anybody explain to me this case?
Thanks in advance. Any help would be appreciated.
Ok after following #peeskillet comment. Here is my finally code.
Check exam notification API
#GET
#Produces(SseFeature.SERVER_SENT_EVENTS)
#Path("/checkExamNotification")
public EventOutput checkExamNotification(#QueryParam("accountId") final int accountId, #QueryParam("sessionId") final String sessionId) {
final EventOutput eventOutput = new EventOutput();
if (memCachedClient.checkSession(sessionId, accountId)) {
new Thread(new Runnable() {
public void run() {
try {
if (examNotificationQueue.hasItems()) {
ExamNotificationQueueItemModel examNotificationQueueItemModel = examNotificationQueue.dequeue();
if (examNotificationQueueItemModel.getAccountId() == accountId) {
LOGGER.info("[START] Check exam notification API");
LOGGER.info("Account ID: " + accountId);
LOGGER.info("Get notification with exam ID: " + examNotificationQueueItemModel.getExamName());
String result = gson.toJson(examNotificationQueueItemModel);
final OutboundEvent.Builder eventBuilder
= new OutboundEvent.Builder();
eventBuilder.data(result);
final OutboundEvent event = eventBuilder.build();
eventOutput.write(event);
LOGGER.info("[END]");
} else {
examNotificationQueue.enqueue(examNotificationQueueItemModel);
}
}
} catch (IOException e) {
throw new RuntimeException(
"Error when writing the event.", e);
} finally {
try {
eventOutput.close();
} catch (IOException ioClose) {
throw new RuntimeException(
"Error when closing the event output.", ioClose);
}
}
}
}).start();
}
return eventOutput;
}
Index.js
function checkExamNotification() {
var url = contextPath + '/api/notification/checkExamNotification?accountId=' + accountId + '&sessionId=' + sessionId;
var source = new EventSource(url);
source.onmessage = function (event) {
displayNumberOfNotification();
};
}