I'm trying to send a file using websocket connection to my server:
I've implemented the websocket server side using java Spring
the client side is in javaScript
for some reason each time i send a binary message from client side using "blob" or "arraybuffer. the server recognise the message as text message not as binary.
what am i missing here?
Client Side
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Chat</title>
</head>
<body>
<h2>File Upload</h2>
Select file
<input type="file" id="filename" />
<br><br><br>
<input type="button" value="Connect" onclick="WebSocketTest()" />
<br><br><br>
<input type="button" value="Upload" onclick="sendFile()" />
<script>
"use strict"
var ws;
function WebSocketTest()
{
if ("WebSocket" in window)
{
console.log("WebSocket is supported by your Browser!");
// Let us open a web socket
ws = new WebSocket("ws://xx.xx.xx.xx:yyyy/service/audioHandler");
ws.onopen = function()
{
// Web Socket is connected, send data using send()
ws.send(JSON.stringify({userName:'xxxx',password:'sgdfgdfgdfgdfgdf'}));
console.log("Message is sent...");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
console.log("Message is received...");
};
ws.onclose = function()
{
// websocket is closed.
console.log("Connection is closed...");
};
}
else
{
// The browser doesn't support WebSocket
console.log("WebSocket NOT supported by your Browser!");
}
}
function sendFile() {
var file = document.getElementById('filename').files[0];
ws.binaryType = "arraybuffer";
//ws.send('filename:'+file.name);
var reader = new FileReader();
var rawData = new ArrayBuffer();
console.log(file.name);
reader.loadend = function() {
}
reader.onload = function(e) {
rawData = e.target.result;
ws.send(rawData);
console.log("the File has been transferred.")
//ws.send('end');
}
reader.readAsArrayBuffer(file);
}
</script>
</body>
</html>
Server Side
public class WebSocketController extends BinaryWebSocketHandler {
#Autowired
private ApplicationContext applicationContext;
#Autowired
private CallScopeStore callScopeStore;
private static Logger logger = Logger.getLogger(AudioHandler.class);
private static final String STOP_MESSAGE = "stop";
#Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
try {
//do something....
} catch (Exception e) {
logger.error(e, e);
throw new RuntimeException(e);
}
}
#Override
protected void handleTextMessage(final WebSocketSession session, TextMessage message) {
try {
//do something....
}
} catch (Exception e) {
logger.error(e, e);
throw new RuntimeException(e);
}
}
}
You have to send the file as a blob.
ws = new WebSocket("ws://xx.xx.xx.xx:yyyy/service/audioHandler");
ws.binaryData = "blob";
ws.send(message); // Blob object
Probably you can find an error as you mention: "CloseStatus[code=1009, reason=No async message support and buffer too small. Buffer size: [8,192], Message size: [7,816,684]]". That happens because your WebSocket Engine need to be configured to allow binary message with the size you want. For example, in your WebSocketConfig:
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(500000);
container.setMaxBinaryMessageBufferSize(500000);
return container;
}
As you can see, you can set the maximun size allowed for your text and binary messages, just specify a size bigger than 7,816,684 (the file you was trying to send). By default, your buffer size is [8,192] so if you send a file with a smaller size than your buffer size, there shouldn't be problems. For more information, you can check websocket spring documentation.
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(500000);
container.setMaxBinaryMessageBufferSize(500000);
return container;
}
WebSocketMessageBroker invalid. #robert08
#Configuration
#EnableWebSocketMessageBroker
public class MyWebSocketMessageBrokerConfigurer implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setPathMatcher(new AntPathMatcher("."));
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic", "/queue");
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration webSocketTransportRegistration) {
webSocketTransportRegistration
.setMessageSizeLimit(1024 * 1024)
.setSendBufferSizeLimit(1024 * 1024 );
}
}
Related
Scenario:
Client(angular js 1.4) will call Rest endpoint to get data, the server(spring boot) will process the list of files and will return accurate data. To process the list of files, the server will take time depending on the number of files. so I have implements STOMP notification as to send a notification to the client saying "List of files have been processed and here is the bunch of files(result)".
Issue:
Stomp connection is established successfully and client also gets subscribed, but when the server publishes the events, client is not able to receive.
Below is my code snippet:
WebSocketConfig.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic"); // Enables a simple in-memory broker
}
}
SocketController.java
#Controller
public class SocketController {
#SendTo("/topic/public")
public String sendMessage() {
LOGGER.info("====> chatMessage()");
return "List updated successfully";
}
}
main.js
connect() {
var socket = new SockJS('/ws');
var stompClient = Stomp.over(socket);
event.preventDefault();
console.log("socket: ", socket);
console.log("stompClient: ", stompClient);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/public', function (payload) {
console.log("payload: ", payload);
var message = JSON.parse(payload.body);
console.log("message: ", message);
});
}, function (error) {
console.log("onError() called");
console.log("error: ",error);
});
};
Scripts used:
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
connect() method of main.js is called on button click. Probably there is an issue regarding scope I guess.
Use SimpMessagingTemplate to convert and send messages to the specific topic.
#Controller
public class SocketController {
private static final Logger LOGGER = LoggerFactory.getLogger(SocketController.class);
#Autowired private SimpMessagingTemplate template;
public void sendMessage(String message) {
LOGGER.info("====> sendMessage:");
this.template.convertAndSend("/topic/public", message);
}
}
My goal is to send one request from frontend to backend and receive multiple responses. I'm using WebSocket because responses are very frequent and WebSocket seems to be best protocol to do it and SseEmitter send multiple responses from backend.
Here is my request controller:
#MessageMapping("/emitter")
#SendTo("/topic/response")
public SseEmitter output(RunData runData) throws Exception {
SseEmitter emitter = new SseEmitter();
new Thread(new Runnable() {
#Override
public void run() {
try {
RemoteHostController rhc = new RemoteHostController(runData);
rhc.execute();
while (rhc.getActiveCount() > 0) {
emitter.send(rhc.getAllOutput());
Thread.sleep(2000);
}
emitter.complete();
} catch (Exception ee) {
ee.printStackTrace();
emitter.completeWithError(ee);
}
}
}).start();
return emitter;
}
RemoteHostController is managing connections and getAllOutput returns output from the hosts.
Frontend application is running quite simple index.html that is connecting to websocket using Stomp and SockJS, sends data to server and generate tag with data from response:
function connect() {
var socket = new SockJS('http://localhost:8080/emitter');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/response', function(greeting){
showOutput(greeting.body);
});
});
}
function sendData() {
var hostname = document.getElementById('hostname').value;
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
var command = document.getElementById('command').value;
stompClient.send("/app/emitter", {}, JSON.stringify({ 'hostname': hostname,
'username': username,
'password': password,
'command': command}));
}
function showOutput(message) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
response.appendChild(p);
}
When I send data to backend only response I get is:
{"timeout":null}
It's SseEmitter timeout field, when I change timeout it will return {"timeout":<timeout_value>}.
I can see in logs that RemoteHostController is connecting to hosts and executing commands properly.
Am I doing something wrong? Or WebSocket only supports one request one response communication?
Here is an example of both WebSocket and SSE. As noted above SSE is not supported by IE browsers. Adding as much as I can for completeness. Make sure you are not using a RestController when you use SeeEmitter because that will return the object and that is my guess from the description above.
pom.xml
<dependencies>
<!-- Spring boot framework -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
Web socket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class ApplicationWebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
super.configureMessageBroker(registry);
registry.enableSimpleBroker("/topic");
}
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/socketrequest").withSockJS();
}
}
Request Data:
public class RequestData {
private String string1;
private String string2;
// excluding getters and setters
}
Web socket controller:
#Controller
public class WebSocketController {
#Autowired
SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/processrequest")
void runWebSocket( RequestData requestData ) {
new Thread(new RunProcess(requestData)).start();
}
private class RunProcess implements Runnable {
private RequestData requestData;
RunProcess(RequestData requestData) {
this.requestData = requestData;
}
public void run() {
simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString1());
simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString2());
simpMessagingTemplate.convertAndSend("/topic/response", "A third response via websocket");
}
}
}
Sse Controller:
#Controller
public class SseController {
#RequestMapping("/emitter")
public SseEmitter runEmitter(#RequestParam(value = "string1") String string1,
#RequestParam(value = "string2") String string2)
{
SseEmitter sseEmitter = new SseEmitter();
RequestData requestData = new RequestData();
requestData.setString1(string1);
requestData.setString2(string2);
new Thread(new RunProcess(requestData,sseEmitter)).start();
return sseEmitter;
}
private class RunProcess implements Runnable {
private RequestData requestData;
private SseEmitter sseEmitter;
RunProcess(RequestData requestData, SseEmitter sseEmitter) {
this.requestData = requestData;
this.sseEmitter = sseEmitter;
}
public void run() {
try {
sseEmitter.send(requestData.getString1());
sseEmitter.send(requestData.getString2());
sseEmitter.send("A third response from SseEmitter");
sseEmitter.complete();
} catch (IOException e) {
e.printStackTrace();
sseEmitter.completeWithError(e);
}
}
}
}
Html code:
<script src="/javascript/sockjs-0.3.4.js"></script>
<script src="/javascript/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
function connect() {
var socket = new SockJS('http://localhost:8085/socketrequest');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/response', function(message){
showOutput(message.body);
});
});
}
function doWebsocket() {
stompClient.send("/processrequest", {}, JSON.stringify({ 'string1': 'The first string', 'string2' : 'The second string' }));
}
function doSse() {
console.log("doSse");
var rtUrl= '/emitter?string1=first string sse&string2=second string sse';
var source = new EventSource(rtUrl);
source.onmessage=function(event){
showOutput(event.data)
};
}
function showOutput(message) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
response.appendChild(p);
}
connect();
</script>
</head>
<div>
Starting page
</div>
<div>
<button id="websocket" onclick="doWebsocket();">WebSocket</button>
<button id="sse" onclick="doSse();">Server Side Events</button>
</div>
<div >
Response:
<p id="response"></p>
</div>
</html>
I want to send an alerts to client if anything goes wrong in server side coding using STOMP Client. I have searched in google and didn't find any examples but I did find STOMP client documentation.
I have written following code in controller
WebSocketClient transport = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(transport);
stompClient.setMessageConverter(new StringMessageConverter());
String url = "ws://localhost:8080/SpringMVCStompClient/alert";
StompSessionHandler handler = new MySessionHandler();
stompClient.connect(url, handler);
and MyHandler code is:
public class MySessionHandler extends StompSessionHandlerAdapter {
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
Alert alert= new Alert();
alert.setName("Server");
alert.setMessage("Message from server");
session.send("/topic/alert", alert);
}
}
when i am trying to execute this execution is not going to MyHandler class. Is this is the right way to do?. Help he me how can i send a alert to client.
Websocket XML Configuration
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/alert">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
Controller
#Autowired
public SpringMVCController(SimpMessagingTemplate template) {
this.template = template;
}
#Autowired
private SimpMessagingTemplate template;
#MessageMapping("/alert")
#SendTo("/topic/alert")
public Alerts alerts(Alerts alerts) throws Exception {
Alert alert= new Alert();
alert.setName("Server");
alert.setMessage("Message from server");
this.template.convertAndSend("/topic/alert", alert);
return alerts;
}
Client js code service
(function(angular, SockJS, Stomp) {
angular.module("alertApp.services").service("AlertService", function($q, $timeout) {
var service = {}, listener = $q.defer(), socket = {
client: null,
stomp: null
};
service.SOCKET_URL = "/SpringMVCStompClient/alert";
service.ALERT_TOPIC = "/topic/alert";
service.ALERT_BROKER = "/app/alert";
service.receive = function() {
return listener.promise;
};
service.send = function(alert) {
socket.stomp.send(service.ALERT_BROKER, { priority: 9}, JSON.stringify({
message: alert.message,
name: alert.name
}));
};
service.disconnect = function() {
socket.stomp.disconnect();
};
var startListener = function() {
socket.stomp.subscribe(service.ALERT_TOPIC, function(data) {
listener.notify(JSON.parse(data.body));
});
};
service.initialize = function() {
socket.client = new SockJS(service.SOCKET_URL);
socket.stomp = Stomp.over(socket.client);
socket.stomp.connect({}, startListener);
};
return service;
});
})(angular, SockJS, Stomp);
I am trying to design a Java Servlet for the first time.
This is my final target behavior.
Servlet receives a request.
Servlet makes a HTTP Request to another server (Lets call it MServer).
MServer can asynchronously send replies to my Servlets request within the next 20-25 mins.
I want to send this data back to the user (who made the request to the servlet in step 1) as soon as the Servlet receives this data.
Any idea how I might do this?
As of now I have just made a "Hello World" Java Servlet. Also code to communicate with MServer is ready. But I dont know how can I achieve this asynchronous behavior.
Any help would be greatly appreciated.
I think you should use an asynchronous servlet. I assume there is an MServer facade that looks like this:
interface MServer {
void getStuff(Observer observer);
}
interface Observer {
void onNewStuff(String stuff);
void onThatWasAllStuff();
}
class MServerSingleton {
MServer getInstance() {
// Code that returns an MServer.
}
}
Then you can implement your servlet something like this:
public class TheServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, final HttpServletResponse resp) {
resp.setContentType("text/plain");
final AsyncContext context = req.startAsync();
MServerSingleton.getInstance().getStuff(new Observer() {
void onNewStuff(String stuff) {
try {
resp.getWriter().write(stuff + "\n\n");
resp.getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
}
void onThatWasAllStuff() {
context.complete();
}
});
}
}
You will likely need to adjust timeouts for this to work.
You might want to consider using Server Sent Events.
Here is a sample hope it helps. It assumes that you have a browser as a client and implements jQuery Ajax call. It checks every 5 seconds if your long running call from Mserver is done and data is available for client to use. I hope it helps you :)
Your servlet code:
public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
#Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String data = Mserver.getInstance().getData();
response.setContentType("text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.println(data);
}
}
Sample Mserver code:
public class Mserver {
private String data;
private static final Mserver mserver = new Mserver();
public static Mserver getInstance() {
return mserver;
}
private Mserver() {
new Thread() {
#Override
public void run() {
computeData();
}
}.start();
}
// Computing data by making multiple server calls etc..
private void computeData() {
try {
System.out.println("Waiting for 20 seconds simulating long running call");
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
data = "Computed Data is ready now.";
}
public String getData() {
return data;
}
}
Html page using jQuery Ajax calls:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="js/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8">
$(document).ready(function() {
setInterval(function() {
$.ajax({
type:"get",
url: "checkDataFromMserver",
async: false,
success: function (data) {
$("#response").html(data);
}
});
}, 5000);
});
</script>
</head>
<body>
Getting data using jQuery Ajax
<div id="response"></div>
</body>
</html>
I tested it and it works. The client keeps polling every 5 seconds to check if data is ready. And after 20 seconds it gets it's data from Mserver.
Hope you find it useful!
You will probably have to implement a continuous check on the client's side and a queue on the server side. In other words the client will be contacting the servlet every 1-2 minutes to check for new messages addressed to him. The servlet should also have a method to get the data asynchronously and then store the recieved data in the queue. Implementing such queue can be done in many ways, for example by storing the responses from MServer in a database table.
I want to check size of uploading files and prevent files loaded in memory entirely. I'm using CommonsMultipartFile. The uploaded file will be processed and persisted in DB. AbstractCoupleUploadController class handles incoming request that containing files:
public abstract class AbstractCoupleUploadController<T extends Serializable> extends RemoteServiceServlet implements ServletContextAware,
UploadServlet<WorkshopHistoryModel>
{
...
#RequestMapping(method={RequestMethod.GET,RequestMethod.POST})
public ModelAndView handleRequest(#RequestParam("firstFile") CommonsMultipartFile firstFile,
#RequestParam("secondFile") CommonsMultipartFile secondFile, HttpServletRequest request, HttpServletResponse response)
{
synchronized(this)
{
initThreads();
perThreadRequest.set(request);
perThreadResponse.set(response);
}
handleUpload(firstFile,secondFile,request,response);
response.getWriter().flush();
response.flushBuffer();
return null;
}
private void handleUpload(CommonsMultipartFile firstFile, CommonsMultipartFile secondFile, HttpServletRequest request,
HttpServletResponse response) throws IOException
{
response.setContentType("text/html");
if(firstFile.getSize() == 0 || secondFile.getSize() == 0)
{
response.getWriter().print(AppConstants.UPLOAD_ZERO_SIZE_FILE);
return;
}
// other validations
// uploading:
try
{
String content = request.getParameter(CoupleUploadPanel.CONTENT);
T model = deserialize(content);
UploadResultModel resultModel = upload(model,firstFile,secondFile); // it's implemented in UploadFileServletImpl
if(resultModel.hasCriticalError())
{
response.getWriter().print(AppConstants.UPLOAD_FAIL + "," + String.valueOf(resultModel.getWorkshopHistoryId()));
}
else
{
response.getWriter().print(AppConstants.UPLOAD_SUCCESS + "," + String.valueOf(resultModel.getWorkshopHistoryId()));
}
}
catch(ProcessRequestException e)
{
// write upload error description in response.getWriter()
}
catch(Exception e)
{
e.printStackTrace();
response.getWriter().print(AppConstants.UPLOAD_UNKOWN_ERROR);
}
}
...
}
I have a multipartResolver bean in my app-servlet.xml (file.upload.max_size=9437184), And also a maxUploadSizeExceededExceptionHandler bean for handling UploadSizeExceededExceptions:
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="${file.upload.max_size}" />
</bean>
<bean id="maxUploadSizeExceededExceptionHandler" class="com.insurance.ui.server.uploadfile.MaxUploadSizeExceededExceptionHandler">
<property name="order" value="1"/>
</bean>
My maxUploadSizeExceededExceptionHandler:
public class MaxUploadSizeExceededExceptionHandler implements HandlerExceptionResolver, Ordered
{
private int order;
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
{
if(ex instanceof MaxUploadSizeExceededException)
{
try
{
response.getWriter().print(ErrorConstants.UPLOAD_SIZE_EXCEED + "," + (((MaxUploadSizeExceededException) ex).getMaxUploadSize()/(1024*1024)));
response.getWriter().flush();
response.flushBuffer();
return new ModelAndView();
}
catch(IOException e)
{
}
}
return null;
}
...
}
When i upload a very large file (more than ${file.upload.max_size}, about 700MB), CommonsMultipartResolver throws MaxUploadSizeExceededException immediately which I'm catching and handling it (writing in response.getWriter()) .But My problem: My browser upload-progress bar shows the file is still uploading!! Why?
UPDATE: I'm using:
Spring-*-3.0.5.RELEASE
commons-fileupload-1.1.1
and also tried:
Spring-*-3.1.2.RELEASE
commons-fileupload-1.3
and my AS:
Tomcat 6 (in development)
Jboss 7 (in production)
UPDATE 2: In the client side, i'm using GWT (I think it doesn't matter):
Uploading is started by clicking submitRequestButton:
#UiHandler("submitRequestButton")
public void submitRequestButtonClick(ClickEvent event)
{
try
{
// some validation
submitRequestButton.setEnabled(false);
uploadPanel.upload(model.getWorkshopHistoryModel()); // uploadPanel is from the CoupleUploadPanel type
}
catch(ValidationException exception)
{
// handle validation errors
}
catch(SerializationException e)
{
// handle serialization errors
}
}
I have a CoupleUploadPanel Widget for uploading (two files):
public class CoupleUploadPanel<T extends Serializable> extends FormPanel
{
public final static String CONTENT = "content";
private static final String FIRST_FILE = "firstFile";
private static final String SECOND_FILE = "secondFile";
private Hidden contentInput;
private FileUpload firstFileUploadInput;
private FileUpload secondFileUploadInput;
private SerializationStreamFactory factory;
public CoupleUploadPanel(UploadServletAsync<T> factory)
{
this(null,factory);
}
public CoupleUploadPanel(String url, UploadServletAsync<T> factory)
{
this.factory = (SerializationStreamFactory) factory;
if(url != null)
{
setAction(url);
}
init();
}
public CoupleUploadPanel(String target, String url, UploadServletAsync<T> factory)
{
super(target);
this.factory = (SerializationStreamFactory) factory;
if(url != null)
{
setAction(url);
}
init();
}
private void init()
{
setMethod("POST");
setEncoding(ENCODING_MULTIPART);
firstFileUploadInput = new FileUpload();
firstFileUploadInput.setName(CoupleUploadPanel.FIRST_FILE);
secondFileUploadInput = new FileUpload();
secondFileUploadInput.setName(CoupleUploadPanel.SECOND_FILE);
contentInput = new Hidden();
contentInput.setName(CONTENT);
VerticalPanel panel = new VerticalPanel();
panel.add(firstFileUploadInput);
panel.add(secondFileUploadInput);
panel.add(contentInput);
add(panel);
}
public void upload(T input) throws SerializationException
{
contentInput.setValue(serialize(input));
submit();
}
private String serialize(T input) throws SerializationException
{
SerializationStreamWriter writer = factory.createStreamWriter();
writer.writeObject(input);
return writer.toString();
}
}
We should pass a UploadServletAsync to CoupleUploadPanel constructor. UploadServletAsync and UploadServlet interfaces:
public interface UploadServletAsync<T extends Serializable>
{
void upload(T model, AsyncCallback<Void> callback);
}
public interface UploadServlet<T extends Serializable> extends RemoteService
{
void upload(T model);
}
So the uploadPanel will be instantiated in this way:
uploadPanel= new CoupleUploadPanel<WorkshopHistoryModel>((UploadFileServletAsync) GWT.create(UploadFileServlet.class));
uploadPanel.setAction(UploadFileServlet.URL);
And a SubmitCompeleteHandler added to uploadPanel (onSumbitComplete() will be called when submit completed and results passed to the client side):
uploadPanel.addSubmitCompleteHandler(new SubmitCompleteHandler()
{
#Override
public void onSubmitComplete(SubmitCompleteEvent event)
{
String s = event.getResults(); //contains whatever written by response.getWriter()
if(s == null)
{
// navigate to request list page
}
else
{
String[] response = s.split(",");
// based on response:
// show error messages if any error occurred in file upload
// else: navigate to upload result page
}
}
});
UploadFileServlet and UploadFileServletAsync interfaces:
public interface UploadFileServlet extends UploadServlet<WorkshopHistoryModel>
{
String URL = "**/uploadFileService.mvc";
}
public interface UploadFileServletAsync extends UploadServletAsync<WorkshopHistoryModel>
{
public static final UploadFileServletAsync INSTANCE = GWT.create(UploadFileServlet.class);
}
In the server side: the UploadFileServletImpl extends AbstractCoupleUploadController and implements the upload() method (upload process):
#RequestMapping(UploadFileServlet.URL)
public class UploadFileServletImpl extends AbstractCoupleUploadController<WorkshopHistoryModel>
{
...
#Override
protected UploadResultModel upload(WorkshopHistoryModel model, MultipartFile firstFile, MultipartFile secondFile)
throws ProcessRequestException
{
return workshopHistoryService.submitList(model.getWorkshop(),firstFile,secondFile);
}
...
}
Well, afaik Spring (a servlet and some filters) does not observe the uploading process, but only handle the result of the finished process. That's because the upload is processed by Tomcat itself (hint: there is an upload size limit option in the web.xml). So it can be possible to make an upload failure (which Spring would not be noticed) or to upload a file which is too large. And only when 2nd happend, the specific filter/interceptor could deny the process.
In my last setup, I had used Nginx as proxy in front of a Tomcat:
If your browser sends the actual file size (modern browsers do, at least IE7? or IE8? does not), than Nginx will send a 500 if the size exceeds the defined limit.
I'm not 100% sure: If the uploaded size exceeds the specified limit, Nginx will send a 500 too. That would cancel the underlaying connection to the Tomcat, too.
We use the following method:
public class MultipartResolver extends CommonsMultipartResolver {
public MultipartHttpServletRequest resolveMultipart(HttpServletRequest request) throws MultipartException {
String encoding = determineEncoding(request);
FileUpload fileUpload = prepareFileUpload(encoding);
try {
List fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
MultipartParsingResult parsingResult = parseFileItems(fileItems, encoding);
return new DefaultMultipartHttpServletRequest(
request, parsingResult.getMultipartFiles(), parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
} catch (FileUploadBase.SizeLimitExceededException ex) {
throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
}
catch (FileUploadException ex) {
throw new MultipartException("Could not parse multipart servlet request", ex);
}
}
public void cleanupMultipart(MultipartHttpServletRequest request) {
super.cleanupMultipart(request);
}
public void setFileSizeMax(long fileSizeMax) {
getFileUpload().setSizeMax(-1);
getFileUpload().setFileSizeMax(fileSizeMax);
}
}
As a first try I would call response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR) in MaxUploadSizeExceededExceptionHandler.
Then I would check one or two SO questions to see whether they would contain some helpful information I could try out.
If this doesn't help I would investigate the sources of GwtUpload and see how they implemented it (or just start using their implementation).
yourfile.getFile().getSize() > Long.parseLong(153600);
This code will approve to upload a file less than 150 kb.
If it exceeds 150 kb you can send any error msg.