Struts2 Result type for Image - struts2

In Struts2 Application I tried to have my Custome Result Type. but am getting no Effect, My JSP page image based action is not getting called.And No Exception am getting also.Please correct me where am doing wrong.
HTTPFox says 404 but am not getting anything in JAVA Console.
HTML :
<img src=" <s:url action='ExternalImageAction' />" />
XML :
<package name="externalImage_package" extends="struts-default">
<result-types>
<result-type name="myBytesResult" class="leo.struts.CustomeImageResult" />
</result-types>
<action name="ExternalImageAction" class="leo.struts.ExternalImageAction">
<result name="myImageResult" type="myBytesResult">
</result>
</action>
</package>
HTTPFOX :
00:18:06.762 0.044 432 1258 GET 404 text/html (NS_ERROR_FAILURE) http://localhost:8888/Struts2Whole/%3Cs:url%20action=%27ExternalImageAction%27%20/%3E
CustomeImageResult:
public void execute(ActionInvocation invocation) throws Exception {
ExternalImageAction action = (ExternalImageAction) invocation.getAction();
HttpServletResponse response = ServletActionContext.getResponse();
response.setContentType(action.getContentType());
response.getOutputStream().write(action.getImageInBytes());
response.getOutputStream().flush();
}
ExternalImageAction :
public String execute()
{
System.out.println("execute of the ExternalImageAction...........");
setContentType("jpg");
setImageInBytes(getFileBytes("C:/Users/Joseph.M/Desktop/ocwcd5.jpg"));
return "myImageResult";
}
public static byte[] getFileBytes(String filePath)
{
File file = new File(filePath);
System.out.println("file : "+file.getName());
byte[] b = new byte[(int) file.length()];
try {
FileInputStream fileInputStream = new FileInputStream(file);
fileInputStream.read(b);
for (int i = 0; i < b.length; i++) {
System.out.print((char)b[i]);
}
fileInputStream.close();
} catch (FileNotFoundException e) {
System.out.println("File Not Found.");
e.printStackTrace();
}
catch (IOException e1) {
System.out.println("Error Reading The File.");
e1.printStackTrace();
}
System.out.println("byes of image size : "+b.length);
return b;
}

If you return something to the src attribute of an <img /> tag, it thinks it is an URL, try to open it and receives 404 Not Found.
Since you are not returning an URL, but the actual image in a byte array, you need to use a Data URI scheme as defined in RFC 2397.
Assuming your result only return the bytes, you should put the Data URI in the html, like described here: https://stackoverflow.com/a/20019398/1654265
Otherwise, you could return the complete Data URI (that must include the Mime Type) in the struts result itself, and keeping your current JSP unchanged.
Simply turn the byte[] to a Base64 String with Apache Commons's encodeBase64URLSafeString, append it to a String like data:image/jpeg;base64, and return that.

Related

Why does do I get a 404 when trying to post content from a zip file vs a normal text file?

I have an MVC cross Web API app that has an ApiFileController, headed:
[Produces("application/json")]
[Route("api/File")]
public class ApiFileController : ApiBaseController
It has the following action method
[HttpPost("PostDir")]
[DisableRequestSizeLimit]
public async Task<IActionResult> PostDir(string serverPath)
{
using (var fileStream = new FileStream(serverPath, FileMode.Create, FileAccess.Write))
{
await Request.Body.CopyToAsync(fileStream);
}
return Ok();
}
that is supposed to received a zipfile containing a directory, and unzip it into the serverPath parameter. Yet when I try and post a file as follows:
sourcePath = Path.Combine("Temp", Guid.NewGuid() + ".zip");
System.IO.Compression.ZipFile.CreateFromDirectory(localPath, sourcePath, CompressionLevel.Fastest, true);
...
using (var fileStream = File.Open(sourcePath, FileMode.Open, FileAccess.Read))
{
using (var reader = new StreamReader(fileStream))
using (var content = new StreamContent(reader.BaseStream))
{
var uri = $"api/File/PostDir?serverPath={WebUtility.UrlEncode(serverPath)}";
var resp = await _client.PostAsync(uri, content);
resp.EnsureSuccessStatusCode();
}
}
I get a 404 - Not found. If I post a plain text file, as follows,
using (var fileStream = File.Open(localPath, FileMode.Open, FileAccess.Read))
{
using (var reader = new StreamReader(fileStream))
using (var content = new StreamContent(reader.BaseStream))
{
var uri = $"api/File/PostDir?serverPath={WebUtility.UrlEncode(serverPath)}";
var resp = await _client.PostAsync(uri, content);
resp.EnsureSuccessStatusCode();
}
}
where localPath points to a plain text file, the PostDir action is correctly invoked and properly saves the text file.
I am using HttpClient, in a wrapper class, to make the requests, and it is initialized in the wrapper's ctor as follows:
public ApiClient()
{
var baseAddress = ConfigurationManager.AppSettings["ApiUrl"];
_client = new HttpClient();
_client.BaseAddress = new Uri(baseAddress);
_client.DefaultRequestHeaders.Clear();
_client.DefaultRequestHeaders.ConnectionClose = false;
_client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
I suspect that when posting the binary zip files, I am missing a content type header or such, but have no idea what I am missing or doing wrong. Could someone please help?
Don't take my code as gospel on how to upload a zip file, but it turns out the error was that I had only used the [DisableRequestSizeLimit] attribute on the action method, and that only disables Kestrel's request size limit. IIS still has a 30MB limit which I disabled by adding a web.config with the following:
<system.webServer>
<security>
<requestFiltering>
<!-- 1 GB -->
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>

Unable to hide primefaces blockui after pdf generation

I am generating pdf and displaying it in separate window/tab using the approach described in The BalusC Code: PDF handling.I need to display blockui ajax loader when i select the commandlink to display pdf.The pdf gets generated but the ajax loader image remains as it is.I need to manually refresh the page to hide it.Is there any way using which it can be hidden as soon as the pdf gets displayed.
My code snippet is as below
JSF page
<h:form id="subFrm">
<p:commandLink value="Download PDF" action="#{pdfBean.downloadPDF}"
onclick="blkUi.show()" oncomplete="blkUi.hide()" id="cmdLink"
ajax="false" />
<p:blockUI block="subFrm" trigger="cmdLink" widgetVar="blkUi">
processing...<br />
<p:graphicImage value="/images/ajaxLoader.gif" />
</p:blockUI>
</h:form>
snippet of Managed bean which is of request scope
#ManagedBean
#RequestScoped
public class PdfBean {
// Constants ----------------------------------------------------------------------------------
private static final int DEFAULT_BUFFER_SIZE = 10240; // 10KB.
// Actions ------------------------------------------------------------------------------------
public void downloadPDF() throws IOException {
// Prepare.
FacesContext facesContext = FacesContext.getCurrentInstance();
ExternalContext externalContext = facesContext.getExternalContext();
HttpServletResponse response = (HttpServletResponse) externalContext.getResponse();
String filePath=externalContext.getRealPath("/pdf");
File file = new File(filePath, "modified.pdf");
BufferedInputStream input = null;
BufferedOutputStream output = null;
try {
// Open file.
input = new BufferedInputStream(new FileInputStream(file), DEFAULT_BUFFER_SIZE);
// Init servlet response.
response.reset();
response.setHeader("Content-Type", "application/pdf");
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Disposition", "inline; filename=\"" + "modified.pdf" + "\"");
output = new BufferedOutputStream(response.getOutputStream(), DEFAULT_BUFFER_SIZE);
// Write file contents to response.
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
int length;
while ((length = input.read(buffer)) > 0) {
output.write(buffer, 0, length);
}
// Finalize task.
output.flush();
} finally {
// Gently close streams.
close(output);
close(input);
}
// Inform JSF that it doesn't need to handle response.
// This is very important, otherwise you will get the following exception in the logs:
// java.lang.IllegalStateException: Cannot forward after response has been committed.
//externalContext.redirect(((HttpServletRequest)externalContext.getRequest()).getRequestURI());
facesContext.responseComplete();
/*FacesContext.getCurrentInstance().getExternalContext()
.redirect("index.xhtml");*/
}
// Helpers (can be refactored to public utility class) ----------------------------------------
private static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
// Do your thing with the exception. Print it, log it or mail it. It may be useful to
// know that this will generally only be thrown when the client aborted the download.
e.printStackTrace();
}
}
}
}
You don't need to open the blockui explicitly. Just remove the code from your commandLink
onclick="blkUi.show()" oncomplete="blkUi.hide()"
remove above. The BlockUI will show up and hides itself.

change default format dataExporter in Primefaces

I want to use to generate a pdf dataexporter, use the method preprocessor to insert some content. By giving the type letter size page assimilates well as formats of texts. Then make a page break to put the chart on a new page, right there is the problem that generates the second page with other size and also find a way to change the font size of the text of the exported table.
<h:commandLink>
<p:graphicImage value="/images/pdf.png"/>
<p:dataExporter type="pdf" target="dataTableAddDetalles" fileName="pdf" preProcessor="#{serviciosMB.preProcessPDF}"/>
</h:commandLink>
backing bean
public void preProcessPDF(Object document) throws Exception {
try {
Document pdf = (Document) document;
pdf.open();
pdf.setPageSize(PageSize.LETTER);
ServletContext servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
String logo = servletContext.getRealPath("") + File.separator + "images" + File.separator + "header.gif";
// pdf.add(Image.getInstance(logo));
pdf.add(new Paragraph("EMNI", FontFactory.getFont(FontFactory.HELVETICA, 22, Font.BOLD, new Color(0, 0, 0))));
SimpleDateFormat formato = new SimpleDateFormat("dd/MM/yyyy");
pdf.add(new Phrase("Fecha: " + formato.format(new Date())));
pdf.newPage();
} catch (Exception e) {
//JsfUtil.addErrorMessage(e, e.getMessage());
}
}
You can't do what you want using dataexporter, you need to change your code to:
<h:commandLink actionListener="#{serviciosMB.createPDF}">
<p:graphicImage value="/images/pdf.png" />
</h:commandLink>
And your managed bean:
public void createPDF() {
try { //catch better your exceptions, this is just an example
FacesContext context = FacesContext.getCurrentInstance();
Document document = new Document();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfWriter.getInstance(document, baos);
if (!document.isOpen()) {
document.open();
}
PdfPTable pdfTable = exportPDFTable();
document.add(pdfTable);
//Keep modifying your pdf file (add pages and more)
document.close();
String fileName = "PDFFile";
writePDFToResponse(context.getExternalContext(), baos, fileName);
context.responseComplete();
} catch (Exception e) {
//e.printStackTrace();
}
}
exportPDFTable method:
private PdfPTable exportPDFTable() {
int numberOfColumns = 1;
itemOfList item = null;
PdfPTable pdfTable = new PdfPTable(numberOfColumns);
pdfTable.setWidthPercentage(100);
BaseFont helvetica = null;
try {
helvetica = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.CP1252, BaseFont.EMBEDDED);
} catch (Exception e) {
//font exception
}
Font font = new Font(helvetica, 8, Font.NORMAL);
pdfTable.addCell(new Paragraph("columnName", font));
for (int i = 0; i < lstPdfTable.size(); i++) { //lstPdfTable is the list from your datatable. A List of "itemOfList" type
item = new itemOfList();
item = lstPdfTable.get(i);
//pdfTable.addCell(new Paragraph('any_string_field', font));
pdfTable.addCell(new Paragraph(item.getStringField(), font));
}
return pdfTable;
}
and writePDFToResponse method is:
private void writePDFToResponse(ExternalContext externalContext, ByteArrayOutputStream baos, String fileName) {
try {
externalContext.responseReset();
externalContext.setResponseContentType("application/pdf");
externalContext.setResponseHeader("Expires", "0");
externalContext.setResponseHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
externalContext.setResponseHeader("Pragma", "public");
externalContext.setResponseHeader("Content-disposition", "attachment;filename=" + fileName + ".pdf");
externalContext.setResponseContentLength(baos.size());
OutputStream out = externalContext.getResponseOutputStream();
baos.writeTo(out);
externalContext.responseFlushBuffer();
} catch (Exception e) {
//e.printStackTrace();
}
}
The primefaces documentation (as of 4.0) does not mention any ability to write a custom data exporter, only pre & post processors, which in the case of PDF prevents you from doing extensive modifications to data, etc.
But what you can do is create a package in your project called
org.primefaces.component.export
and copy ExporterFactory.java from primefaces source.
You can then replace the original PDFExporter call with your own implementation.
The exporter implementation is fairly simple. It uses iText library (although an outdated version) and you can easily extend it to your needs.
An obvious problem with this approach is that you may have to be extra careful when (and if) you are updating your primefaces library in the future.

getting Java Bad File Descriptor Close Bug while reading multipart/form-data http body

My web service hosted on Play! framework. I have few image files uploaded from a non-play! framework based client using the standard HTTP client request with content-type of multipart/form-data.
On the web service side, I tried using Play! ApacheMultipartParser to parse the Http.request.body, but failed with the Java IO Bad File Descriptor exception.
The problem seems come from Java MultipartStream, by looking at the following callstack
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:208)
at org.apache.commons.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:976)
at org.apache.commons.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:886)
at java.io.InputStream.read(InputStream.java:85)
I also tried directly reading the http.request.body into a big buffer for experiment, got the same exception. What could be wrong?
The http data sent out from client side is something like the following. On web service side, I could using IO.write to save it to a file w/o any problem.
Content-Type: multipart/form-data; boundary=--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="foo1.jpg"; filename="foo1.jpg"
Content-Length: 5578
Content-Type: image/jpeg
<image data 1 omitted>
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="foo2.jpg"; filename="foo2.jpg"
Content-Length: 327
Content-Type: image/jpeg
<image data 2 omitted>
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f--
I had the exact same issue. The problem lies in the way that Play! handles multipart uploads. Usually you can add a FileUpload to your upload method and get your files there. This helps a lot as you can get the filenames and sizes and all this stuff directly from Play:
public static void uploadFile(File fileUpload) {
String name = fileUpload.getName() // etc.
}
However using this logic prevents you from using the HTTPRequest. So if you use a non-Play way of uploading files (e.g with XMLHTTPRequest) where the automatic mapping to the fileUpload won't work the following thing happens:
Play tries to bind the request to your arguments
Play encounters your File argument and parses the request.
Play finds nothing of use (as it doesn't understand XMLHttpRequest) and maps your File argument to null.
Now the request input stream has already been consumed by Play and you get your "Bad File Descriptor" message.
The solution to this is, to not use any Play! magic, if you want to use the same method for uploading via Form and XMLHttpRequest (XHR). I wanted to use Valum's file uploader script (http://github.com/valums/file-uploader) in addition to my own form based upload method. One uses XHR, the other uses plain multipart form uploads. I created the following method in my controller, that takes the uploaded file from the "qqfile" parameter and works with form based and XHR-Uploads:
#SuppressWarnings({"UnusedDeclaration"})
public static void uploadFile() {
FileUpload qqfile = null;
DataParser parser = DataParser.parsers.get(request.contentType);
if (parser != null) {
// normal upload. I have to manually parse this because
// play kills the body input stream for XHR-requests when I put the file upload as a method
// argument to {#link #uploadFile)
parser.parse(request.body);
#SuppressWarnings({"unchecked"})
ArrayList<FileUpload> uploads = (ArrayList<FileUpload>) request.args.get("__UPLOADS");
for (FileUpload upload : uploads) {
if ("qqfile".equals(upload.getFieldName())) {
qqfile = upload;
break;
}
}
} else {
// XHR upload
qqfile = new FileUpload(new XHRFileItem("qqfile"));
}
if (qqfile == null) {
badRequest();
return;
}
// and now do something with your Fileupload object here (e.g. write it to db or something else)
}
You probably can skip the IF-part of the if, if you split this method into two, so you can use the normal Play! magic for default uploads and use a separate method for your XHR uploads.
I also had to create the XHRFileItem class which just wraps around a file item that is posted via an XMLHttpRequest. You might have to modify it a bit to work with multiple files and your particular file uploader, but nevertheless here it is:
package application.util;
import org.apache.commons.fileupload.FileItem;
import org.jetbrains.annotations.Nullable;
import java.io.*;
import static play.mvc.Http.Request.current;
/**
* An implementation of FileItem to deal with XmlHttpRequest file uploads.
*/
public class XHRFileItem implements FileItem {
private String fieldName;
public XHRFileItem(String fieldName) {
this.fieldName = fieldName;
}
public InputStream getInputStream() throws IOException {
return current().body;
}
public String getContentType() {
return current().contentType;
}
public String getName() {
String fileName = current().params.get(fieldName);
if (fileName == null) {
fileName = current().headers.get("x-file-name").value();
}
return fileName;
}
public boolean isInMemory() {
return false;
}
public long getSize() {
return 0;
}
public byte[] get() {
return new byte[0];
}
public String getString(String s) throws UnsupportedEncodingException {
return s;
}
public String getString() {
return "";
}
public void write(File file) throws Exception {
FileOutputStream fos = new FileOutputStream(file);
InputStream is = getInputStream();
byte[] buf = new byte[64000];
int read;
while ((read = is.read(buf)) != -1) {
fos.write(buf, 0, read);
}
fos.close();
}
public void delete() {
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public boolean isFormField() {
return false;
}
public void setFormField(boolean b) {
}
#Nullable
public OutputStream getOutputStream() throws IOException {
return null;
}
}
Hope this helps, it took me about a day to make this work on my end.

Dynamically Creating PDF in Struts 2

Good Evening ;
I have a problem that I am working on struts2 web application. I am dynamically creating a PDF using data base. i want to show it in a web page but I don`t know how I do it is any one can help me.
Thanks...
Action code:
public class PDFAction extends ActionSupport {
private InputStream inputStream;
public String getPDF(){
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
PdfWriter.getInstance(document, buffer);
document.open();
Paragraph p = new Paragraph();
p.add("INSTITUTO POLITÉCNICO NACIONAL, ESCUELA SUPERIOR DE CÓMPUTO, DIEGO A. RAMOS");
document.add(p);
document.close();
inputStream = new ByteArrayInputStream(buffer.toByteArray());
return SUCCESS;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
Struts.xml:
<action name="getPDF" class="action.PDFAction" method="getPDF">
<result name="success" type="stream">
<param name="inputName">inputStream</param>
<param name="contentType">application/pdf</param>
<param name="contentDisposition">filename="mypdf.pdf"</param>
<param name="bufferSize">2048</param>
</result>
</action>
Try it, it works like a charm, works perfect for me. If you are in doubt read more about stream result type that Struts 2 provides. The answer to this is so simple yet it was hard to get to it.
You can write the content using the input stream or best way is to create custom result type where you can set appropriate header and other things here is a link for some help
Struts2 Custom Result Type
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
PdfWriter.getInstance(document, buffer);
document.open();
////Do your stuff here
document.close();
DataOutput dataOutput = new DataOutputStream(response.getOutputStream());
byte[] bytes = buffer.toByteArray();
response.setContentLength(bytes.length);
for(int i = 0; i < bytes.length; i++)
{
dataOutput.writeByte(bytes[i]);
}
I m using iText for creating pdf. You can put this scriptlet in a jsp and call this jsp to show the pdf generated

Resources