How to send file name with NanoHttpd Response - http-headers

I've successfully achieved file transfer over local network using NanoHttpd. However, I'm unable to send the file name in NanoHttpd Response. The received files have a default name like this: localhost_8080. I tried to attach file name in response header using Content-disposition, but my file transfer failed all together. What am I doing wrong? Here is my implementation:
private class WebServer extends NanoHTTPD {
String MIME_TYPE;
File file;
public WebServer() {
super(PORT);
}
#Override
public Response serve(String uri, Method method,
Map<String, String> header, Map<String, String> parameters,
Map<String, String> files) {
try {
file=new File(fileToStream);
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
MIME_TYPE= URLConnection.guessContentTypeFromName(file.getName());
} catch (IOException ioe) {
Log.w("Httpd", ioe.toString());
}
NanoHTTPD.Response res=new NanoHTTPD.Response(Status.OK, MIME_TYPE, bis);
res.addHeader("Content-Disposition: attachment; filename=", file.getName());
return res;
}
}
Thanks for your help!

You need to specify the response, the MIME type, and the stream of bytes to be sent. After that you just add a header with the file name of the file since its a http method.
Here is a sample code that solves the problem
#Override
public Response serve(String uri, Method method,
Map<String, String> header, Map<String, String> parameters,
Map<String, String> files) {
FileInputStream fis = null;
try {
fis = new FileInputStream(fileName);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
NanoHTTPD.Response res = new NanoHTTPD.Response(Response.Status.OK, "application/vnd.android.package-archive", fis);
res.addHeader("Content-Disposition", "attachment; filename=\""+fileName+"\"");
return res;
}

Related

How to get InputStream from MultipartFormDataInput?

I'm trying to save pdf in wildfly, I'm using RestEasy MultipartFormDataInput provided with wildfly 20.0.1,
but it doesn't work.
This is what I have:
public static Response uploadPdfFile(MultipartFormDataInput multipartFormDataInput) {
// local variables
MultivaluedMap<String, String> multivaluedMap = null;
String fileName = null;
InputStream inputStream = null;
String uploadFilePath = null;
try {
Map<String, List<InputPart>> map = multipartFormDataInput.getFormDataMap();
List<InputPart> lstInputPart = map.get("poc");
for(InputPart inputPart : lstInputPart){
// get filename to be uploaded
multivaluedMap = inputPart.getHeaders();
fileName = getFileName(multivaluedMap);
if(null != fileName && !"".equalsIgnoreCase(fileName)){
try {
// write & upload file to UPLOAD_FILE_SERVER
//here I have the error: Unable to find a MessageBodyReader for media type:
//application/pdf
inputStream = inputPart.getBody(InputStream.class,InputStream.class);
uploadFilePath = writeToFileServer(inputStream, fileName);
}catch (Exception e) {
e.printStackTrace();
}
// close the stream
inputStream.close();
}
}
}
catch(IOException ioe){
ioe.printStackTrace();
}
finally{
// release resources, if any
}
return Response.ok("File uploaded successfully at " + uploadFilePath).build();
}
I'm using postman for test, http POST method, in the body I send: form-data - file and selected the file.pdf.
When I sent the request, I have the next RunTimeException when I try:
inputStream = inputPart.getBody(InputStream.class,null);
I get:
java.lang.RuntimeException: RESTEASY007545: Unable to find a MessageBodyReader for media type: application/pdf and class type org.jboss.resteasy.util.Base64$InputStream
At the moment I am saving the file receiving it in Base64, but I think that with MultipartFormDataInput it is the correct way.
This is what I have when debug:
Thanks for your support.
I solved this changing the InputStream from "org.jboss.resteasy.util.Base64.InputStream"
to "java.io.InputStream"

NPE when trying to use Jetty async HTTP client

When trying to use Firebase Cloud Messaging by Google with the help of non-blocking Jetty HTTP client in a simple test case that I have prepared at GitHub -
private static final HttpClient sHttpClient = new HttpClient();
private static final Response.ContentListener sFcmListener = new Response.ContentListener() {
#Override
public void onContent(Response response, ByteBuffer content) {
if (response.getStatus() != 200) {
return;
}
String body = StandardCharsets.UTF_8.decode(content).toString();
System.out.printf("onContent: %s\n", body);
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
try {
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map result = (Map) results[0];
String error = (String) result.get(FCM_ERROR);
if (FCM_NOT_REGISTERED.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ignore) {
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.onResponseContent(sFcmListener)
.send();
}
but unfortunately the execution fails immediately with NPE:
2017-06-30 10:46:41.312:INFO::main: Logging initialized #168ms to org.eclipse.jetty.util.log.StdErrLog
Exception in thread "main" java.util.concurrent.ExecutionException: java.lang.NullPointerException
at org.eclipse.jetty.client.util.FutureResponseListener.getResult(FutureResponseListener.java:118)
at org.eclipse.jetty.client.util.FutureResponseListener.get(FutureResponseListener.java:101)
at org.eclipse.jetty.client.HttpRequest.send(HttpRequest.java:682)
at de.afarber.fcmnotregistered.Main.main(Main.java:68)
Caused by: java.lang.NullPointerException
at org.eclipse.jetty.io.ssl.SslClientConnectionFactory.newConnection(SslClientConnectionFactory.java:59)
at org.eclipse.jetty.client.AbstractHttpClientTransport$ClientSelectorManager.newConnection(AbstractHttpClientTransport.java:191)
at org.eclipse.jetty.io.ManagedSelector.createEndPoint(ManagedSelector.java:420)
at org.eclipse.jetty.io.ManagedSelector.access$1600(ManagedSelector.java:61)
at org.eclipse.jetty.io.ManagedSelector$CreateEndPoint.run(ManagedSelector.java:599)
at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:128)
at org.eclipse.jetty.util.thread.Invocable$InvocableExecutor.invoke(Invocable.java:222)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:294)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:199)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
at java.lang.Thread.run(Thread.java:745)
Why does it happen please?
UPDATE:
I have switched to using BufferingResponseListener and the NPE is gone, but now the program prints java.net.NoRouteToHostException: No route to host even though the Google FCM endpoint is a well-known host:
private static final HttpClient sHttpClient = new HttpClient();
private static final BufferingResponseListener sFcmListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
System.err.println(result.getFailure()); // No route to host
return;
}
String body = getContentAsString(StandardCharsets.UTF_8);
System.out.printf("onContent: %s\n", body);
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
try {
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map map = (Map) results[0];
String error = (String) map.get(FCM_ERROR);
if (FCM_NOT_REGISTERED.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ex) {
System.err.println(ex);
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.send(sFcmListener);
}
I get the No route to host for any FCM_URL value I try, why?
Adding SslContextFactory has helped me:
private static final SslContextFactory sFactory = new SslContextFactory();
private static final HttpClient sHttpClient = new HttpClient(sFactory);
private static final BufferingResponseListener sFcmListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
System.err.println(result.getFailure());
return;
}
String body = getContentAsString(StandardCharsets.UTF_8);
System.out.printf("onComplete: %s\n", body);
try {
Map<String, Object> resp = (Map<String, Object>) JSON.parse(body);
Object[] results = (Object[]) resp.get(FCM_RESULTS);
Map map = (Map) results[0];
String error = (String) map.get(FCM_ERROR);
System.out.printf("error: %s\n", error);
if (FCM_NOT_REGISTERED.equals(error) ||
FCM_MISSING_REGISTRATION.equals(error) ||
FCM_INVALID_REGISTRATION.equals(error)) {
// TODO delete invalid FCM token from the database
}
} catch (Exception ex) {
System.err.println(ex);
}
}
};
public static void main(String[] args) throws Exception {
sHttpClient.start();
sHttpClient.POST(FCM_URL)
.header(HttpHeader.AUTHORIZATION, FCM_KEY)
.header(HttpHeader.CONTENT_TYPE, "application/json")
.content(new StringContentProvider(JSON.toString(REQUEST)))
.send(sFcmListener);
}
The still open question I have is how to retrieve the invalid FCM token that I have used in the Jetty HTTP client request, so that I can delete it from my database on the response...

spring boot file uploading executes twice when MultipartException occurs

I want to upload files using spring-boot, and I have configured the properties right, and I also ensure the controller is correct, but the strange thing is the controller executed twice when I tried to upload a file larger exceed the limitation, what I expect is an error json message, and what I got is no response under the Postman.
Here is my controller,
#RestController
public class FileUploadController implements HandlerExceptionResolver {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);
private static final String UPLOAD_PATH = "upload";
#ResponseBody
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = "multipart/form-data", produces = "application/json;charset=UTF-8")
public String upload(final MultipartFile file) {
try {
final Result<String> result = new Result<>();
if (file.isEmpty()) {
result.setSuccess(false);
result.setMessage("file is empty");
return Constants.OBJECT_MAPPER.writeValueAsString(result);
}
final File outputFile = new File(UPLOAD_PATH, UUID.randomUUID().toString());
FileUtils.writeByteArrayToFile(outputFile, file.getBytes());
result.setSuccess(true);
result.setMessage(outputFile.toString());
return Constants.OBJECT_MAPPER.writeValueAsString(result);
} catch (final Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ExceptionResultBuilder.build(ex);
}
}
#Override
public ModelAndView resolveException(final HttpServletRequest request, final HttpServletResponse response, final Object handler, final Exception ex) {
final ModelAndView modelAndView = new ModelAndView();
modelAndView.setView(new MappingJackson2JsonView());
final Map<String, Object> map = new HashMap<>();
map.put("success", false);
if (ex instanceof MultipartException) {
// if (LOGGER.isDebugEnabled()) {
LOGGER.info(ex.getMessage(), ex);
// }
final Throwable rootCause = ((MultipartException) ex).getRootCause();
if (rootCause instanceof SizeLimitExceededException) {
map.put("message", "request too large");
} else if (rootCause instanceof FileSizeLimitExceededException) {
map.put("message", "file too large");
} else {
map.put("message", "其他异常: " + rootCause.getMessage());
}
} else {
LOGGER.error(ex.getMessage(), ex);
}
modelAndView.addAllObjects(map);
return modelAndView;
}
}
and this is my property snippet for file uploading,
# MULTIPART (MultipartProperties)
multipart.enabled=true
multipart.max-file-size=5Mb
multipart.max-request-size=10Mb
If I tried to upload a file a bit larger than 5M, I will get the result like below under Postman, (the file size is 5208k)
enter image description here
and if I tried to upload a file between 5M and 10M, I will get this error, (the file size is 9748k)
enter image description here
I debugged into the controller and found that the resolveException method executed twice in a single upload.
Does anybody give me some tip?
The latest code list here, and I still got the same result,
#RestController
#ControllerAdvice
public class FileUploadController {
private static final Logger LOGGER = LoggerFactory.getLogger(FileUploadController.class);
private static final String UPLOAD_PATH = "upload";
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = "multipart/form-data", produces = "application/json;charset=UTF-8")
public HttpEntity<?> upload(final MultipartFile file) {
try {
final Result<String> result = new Result<>();
if (file == null || file.isEmpty()) {
result.setSuccess(false);
result.setMessage("上传的文件为空");
return new ResponseEntity<Result<?>>(result, HttpStatus.OK);
}
final File outputFile = new File(UPLOAD_PATH, UUID.randomUUID().toString());
FileUtils.writeByteArrayToFile(outputFile, file.getBytes());
result.setSuccess(true);
result.setMessage(outputFile.toString());
return new ResponseEntity<Result<?>>(result, HttpStatus.OK);
} catch (final Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ExceptionResultBuilder.build(ex);
}
}
#ExceptionHandler(MultipartException.class)
public HttpEntity<?> multipartExceptionHandler(final MultipartException exception) {
LOGGER.error(exception.getMessage(), exception);
try {
final Result<String> result = new Result<>();
result.setSuccess(false);
final Throwable rootCause = ((MultipartException) exception).getRootCause();
if (rootCause instanceof SizeLimitExceededException) {
result.setMessage("请求过大");
} else if (rootCause instanceof FileSizeLimitExceededException) {
result.setMessage("文件过大");
} else {
result.setMessage("未知错误");
}
return new ResponseEntity<Result<?>>(result, HttpStatus.OK);
} catch (final Exception ex) {
LOGGER.error(ex.getMessage(), ex);
return ExceptionResultBuilder.build(ex);
}
}
}
I just go the same error and fix it by add the flowing code to my controller, good luck
#ExceptionHandler({ MultipartException.class, FileSizeLimitExceededException.class,
SizeLimitExceededException.class })
public ResponseEntity<Attachment> handleUploadrException(HttpServletRequest request, Throwable ex) {
Attachment result = new Attachment();
result.setDescription(ex.getMessage());
HttpStatus status = getStatus(request);
return new ResponseEntity<Attachment>(result, status);
}
private HttpStatus getStatus(HttpServletRequest request) {
Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
if (statusCode == null) {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
return HttpStatus.valueOf(statusCode);
}

GWT: Sending PDF document from server to client

I have an RPC service and one of the method is generating a report using Pentaho Reporting Engine. Report is an PDF file. What I'd like to do, is when user request a report, the report is sent back to him and save dialog or sth pops up. I tried this inside my service method:
Resource res = manager.createDirectly(new URL(reportUrl), MasterReport.class);
MasterReport report = (MasterReport) res.getResource();
report.getParameterValues().put("journalName", "FooBar");
this.getThreadLocalResponse().setContentType("application/pdf");
PdfReportUtil.createPDF(report, this.getThreadLocalResponse().getOutputStream());
But it doesn't work. How it can be done?
I do it a little bit differently. I've got a separate servlet that I use to generate the PDF. On the client, do something like:
Cookies.setCookie(set what ever stuff PDF needs...);
Window.open(GWT.getModuleBaseURL() + "DownloadPDF", "", "");
The servlet, DownloadPDF looks something like this:
public class DownloadPDF extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
Cookie[] cookies = request.getCookies();
try {
// get cookies, generate PDF.
// If PDF is generated to to temp file, read it
byte[] bytes = getFile(name);
sendPDF(response, bytes, name);
} catch (Exception ex) {
// do something here
}
}
byte[] getFile(String filename) {
byte[] bytes = null;
try {
java.io.File file = new java.io.File(filename);
FileInputStream fis = new FileInputStream(file);
bytes = new byte[(int) file.length()];
fis.read(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return bytes;
}
void sendPDF(HttpServletResponse response, byte[] bytes, String name) throws IOException {
ServletOutputStream stream = null;
stream = response.getOutputStream();
response.setContentType("application/pdf");
response.addHeader("Content-Type", "application/pdf");
response.addHeader("Content-Disposition", "inline; filename=" + name);
response.setContentLength((int) bytes.length);
stream.write(bytes);
stream.close();
}
}

JavaFX: File upload to REST service / servlet fails because of missing boundary

I'm trying to upload a file using JavaFX using the HttpRequest. For this purpose I have written the following function.
function uploadFile(inputFile : File) : Void {
// check file
if (inputFile == null or not(inputFile.exists()) or inputFile.isDirectory()) {
return;
}
def httpRequest : HttpRequest = HttpRequest {
location: urlConverter.encodeURL("{serverUrl}");
source: new FileInputStream(inputFile)
method: HttpRequest.POST
headers: [
HttpHeader {
name: HttpHeader.CONTENT_TYPE
value: "multipart/form-data"
}
]
}
httpRequest.start();
}
On the server side, I am trying to handle the incoming data using the Apache Commons FileUpload API using a Jersey REST service. The code used to do this is a simple copy of the FileUpload tutorial on the Apache homepage.
#Path("Upload")
public class UploadService {
public static final String RC_OK = "OK";
public static final String RC_ERROR = "ERROR";
#POST
#Produces("text/plain")
public String handleFileUpload(#Context HttpServletRequest request) {
if (!ServletFileUpload.isMultipartContent(request)) {
return RC_ERROR;
}
FileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = null;
try {
items = upload.parseRequest(request);
}
catch (FileUploadException e) {
e.printStackTrace();
return RC_ERROR;
}
...
}
}
However, I get a exception at items = upload.parseRequest(request);:
org.apache.commons.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
I guess I have to add a manual boundary info to the InputStream. Is there any easy solution to do this? Or are there even other solutions?
Have you tried just using the InputStream from HttpServletRequest like so
InputStream is = httpRequest.getInputStream();
BufferedInputStream in = new BufferedInputStream(is);
//Write out bytes
out.close();
is.close();