Uploading multiple files and metadata with CXF - java

I need to create a file upload handler as a REST web service with CXF. I've been able to upload a single file with metadata using code like the following:
#POST
#Path("/uploadImages")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadImage(#Multipart("firstName") String firstName,
#Multipart("lastName") String lastName,
List<Attachment> attachments) {
for (Attachment att : attachments) {
if (att.getContentType().getType().equals("image")) {
InputStream is = att.getDataHandler().getInputStream();
// read and store image file
}
}
return Response.ok().build();
}
Now I need to add support for uploading multiple files in the same request. In this case, instead of an attachment with image/jpeg content type, I get an attachment with multipart/mixed content type, which itself contains the individual image/jpeg attachments that I need.
I've seen examples for uploading multiple JSON or JAXB objects with metadata, but I have not been able to get anything to work with binary image data. I have tried using the MultipartBody directly, but it only returns the multipart/mixed attachment, not the image/jpeg attachments embedded within it.
Is there a way to recursively parse a multipart/mixed attachment to get the embedded attachments? I can of course get the input stream of the multipart/mixed attachment, and parse out the files myself, but I'm hoping there is a better way.
UPDATE
This seems kludgey, but the following bit of code is good enough for now. I would love to see a better way though.
for (Attachment att : attachments) {
LOG.debug("attachment content type: {}", att.getContentType().toString());
if (att.getContentType().getType().equals("multipart")) {
String ct = att.getContentType().toString();
Message msg = new MessageImpl();
msg.put(Message.CONTENT_TYPE, ct);
msg.setContent(InputStream.class, att.getDataHandler().getInputStream());
AttachmentDeserializer ad = new AttachmentDeserializer(msg, Arrays.asList(ct));
ad.initializeAttachments();
// store the first embedded attachment
storeFile(msg.getContent(InputStream.class));
// store remaining embedded attachments
for (org.apache.cxf.message.Attachment child : msg.getAttachments()) {
storeFile(child.getDataHandler().getInputStream());
}
}
else if (att.getContentType().getType().equals("image")) {
storeFile(att.getDataHandler().getInputStream());
}
}

I've build a similar service to upload multiple images. My implementation looks like the following (maybe it helps)
#Consumes({MediaType.MULTIPART_FORM_DATA,"multipart/mixed" })
public Response uploadImages(final List<Attachment> attachments) {
Map<String, InputStream> imageMap = new HashMap<String, InputStream>();
for (Attachment attachment : attachments) {
String imageName = attachment.getContentDisposition().getParameter("filename");
if (imageName == null) {
imageName = UUID.randomUUID().toString();
}
InputStream image = attachment.getDataHandler().getInputStream();
imageMap.put(imageName, image);
}
return imageMap;
}
if someone prefers bye arrays instead of input streams, it can be converted easily using this helper method
private static byte[] extractByteArray(final InputStream inputStream) throws IOException {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
byte[] dataChunk = new byte[1024 * 16];
int numRead = 0;
while (numRead != -1) {
numRead = inputStream.read(dataChunk, 0, dataChunk.length);
if (numRead != -1) {
buffer.write(dataChunk, 0, numRead);
}
}
buffer.flush();
return buffer.toByteArray();
}

Related

How to temporarily create a text file without any file location and send as a response in spring boot at run time?

Need to create a txt file by the available data and then need to send the file as rest response.
the app is deployed in container. i dont want to store it in any location on container or any location in spring boot resources. is there any way where we can create file at runtime buffer without giving any file location and then send it in rest response?
App is production app so i need a solution which is secure
A file is a file. You're using the wrong words - in java, the concept of a stream of data, at least for this kind of job, is called an InputStream or an OutputStream.
Whatever method you have that takes a File? That's the end of the road. A File is a file. You can't fake it. But, talk to the developers, or check for alternate methods, because there is absolutely no reason anything in java that does data processing requires a File. It should be requiring an InputStream or possibly a Reader. Or perhaps even there is a method that gives you an OutputStream or Writer. All of these things are fine - they are abstractions that lets you just send data to it, from a file, a network connection, or made up whole cloth, which is what you want.
Once you have one of those, it's trivial. For example:
String text = "The Text you wanted to store in a fake file";
byte[] data = text.getBytes(StandardCharsets.UTF_8);
ByteArrayInputStream in = new ByteArrayInputStream(data);
whateverSystemYouNeedToSendThisTo.send(in);
Or for example:
String text = "The Text you wanted to store in a fake file";
byte[] data = text.getBytes(StandardCharsets.UTF_8);
try (var out = whateverSystemYouNeedToSendThisTo.getOUtputStream()) {
out.write(data);
}
Take a look at the function below:
Imports
import com.google.common.io.Files;
import org.springframework.http.ContentDisposition;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import java.io.*;
import java.nio.file.Paths;
Function:
#GetMapping(value = "/getFile", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
private ResponseEntity<byte[]> getFile() throws IOException {
File tempDir = Files.createTempDir();
File file = Paths.get(tempDir.getAbsolutePath(), "fileName.txt").toFile();
String data = "Some data"; //
try (FileWriter fileWriter = new FileWriter(file)) {
fileWriter.append(data).flush();
} catch (Exception ex) {
ex.printStackTrace();
}
byte[] zippedData = toByteArray(new FileInputStream(file));
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentDisposition(ContentDisposition.builder("attachment").filename("file.txt").build());
httpHeaders.setContentType(MediaType.APPLICATION_OCTET_STREAM);
httpHeaders.setContentLength(zippedData.length);
return ResponseEntity.ok().headers(httpHeaders).body(zippedData);
}
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] buffer = new byte[in.available()];
int len;
// read bytes from the input stream and store them in buffer
while ((len = in.read(buffer)) != -1) {
// write bytes from the buffer into output stream
os.write(buffer, 0, len);
}
return os.toByteArray();
}
In a nutshell, you want to store data in memory. Basic building block for this is array of bytes - byte[].
In JDK there are two classes to connect IO world with byte array - ByteArrayInputStream and ByteArrayOutputStream.
Rest is just same, as when dealing with files.
Example 1
#GetMapping(value = "/image")
public #ResponseBody byte[] getImage() throws IOException {
InputStream in = getClass()
.getResourceAsStream("/com/baeldung/produceimage/image.jpg");
return IOUtils.toByteArray(in);
}
Example 2:
#GetMapping("/get-image-dynamic-type")
#ResponseBody
public ResponseEntity<InputStreamResource> getImageDynamicType(#RequestParam("jpg") boolean jpg) {
MediaType contentType = jpg ? MediaType.IMAGE_JPEG : MediaType.IMAGE_PNG;
InputStream in = jpg ?
getClass().getResourceAsStream("/com/baeldung/produceimage/image.jpg") :
getClass().getResourceAsStream("/com/baeldung/produceimage/image.png");
return ResponseEntity.ok()
.contentType(contentType)
.body(new InputStreamResource(in));
}
Ref: https://www.baeldung.com/spring-controller-return-image-file

Image byte stream is different when file is read in different ways

Under my Spring 4.3/Maven 3.3 project I have an image file, a PNG file, at:
src/main/resources/images/account.png
I have a util java application file that reads in an image, and it writes it to the database field. The code is as follows:
private static String _accountFilename = "src/main/resources/images/account.png";
private byte[] getByteArrayFromFile(String filename)
{
FileInputStream fileInputStream = null;
File file = new File(filename);
byte[] bFile = new byte[(int) file.length()];
try
{
// convert file into array of bytes
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
for (int i = 0; i < bFile.length; i++)
{
System.out.print((char) bFile[i]);
}
System.out.println("Done");
}
catch (Exception e)
{
e.printStackTrace();
}
return bFile;
}
public String getImageData(byte[] imageByteArray)
{
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(imageByteArray);
base64 = "data:image/png;base64," + base64;
return base64;
}
The String that comes back from "getImageData" works great. I can put that String in the MySQL database, in a table, and the field is defined as TEXT.
I can pull that base64 encoded data back, and display the image.
Now, If I am calling this code from a Spring Service instead of an application, then the image "src/main/resources/images/account.png" is not found.
After researching on the Net for some time, there are many, many examples of getting a file from "resources" and many of these did not work for me. Since I am in Spring, I tried a few things and finally this worked:
#Value(value = "classpath:images/account.png")
private Resource defaultAccountImage;
private byte[] getByteArrayFromFile(Resource image)
{
InputStream inputStream = null;
byte[] bFile = null;
try
{
bFile = new byte[(int) image.contentLength()];
// convert file into array of bytes
inputStream = image.getInputStream();
inputStream.read(bFile);
inputStream.close();
for (int i = 0; i < bFile.length; i++)
{
System.out.print((char) bFile[i]);
}
System.out.println("Done");
}
catch (Exception e)
{
e.printStackTrace();
}
return bFile;
}
private String getImageData(byte[] imageByteArray)
{
Base64.Encoder encoder = Base64.getEncoder();
String base64 = encoder.encodeToString(imageByteArray);
base64 = "data:image/png;base64," + base64;
return base64;
}
public String getDefaultAccountImage()
{
byte[] accountImage = getByteArrayFromFile(defaultAccountImage);
String fileString = getImageData(accountImage);
return fileString;
}
When I look at the String/Image data between the first way with the standalone java app, and the second way with the #Value and inputstream, there is a definite different in the string data.
part of the string data is similar, but then it drastically changes, and they don't match. As a result the text data for the image from the second method doesn't display as an image.
So, I was hoping I could get this image text data, and it would be the same, but it is not. If I can use my web-service, which calls the business service which calls this ImageUtil code where I use the #Value to get the image resource and it saves the text string correctly, that would be great.
If you have any advice, I would very much appreciate it. Thanks!
UPDATE 1:
This is a multi-maven project:
parent-project
entity
dao
service
ws
When I run my test code within the Service layer, the suggested solution works great! The images are found and the byte string gets loaded as it should be. And then I compiled the code into a jar.
The entity.jar gets created first.
The dao.jar gets created and pulls in the entity.jar.
The service.jar gets created and pulls in the dao.jar. This layer also has the /resources/images/account.png file. But this image is now in the jar.
The ws.WAR file pulls in the service.jar file ...
so the code in the answer does not find the image in the resources.
When I run the tests from the ws layer, I get a FileNotFoundException.
So ... now I am researching on how to get an image from jar ...
Does this change how I should be getting my image byte array?
You can get the file from the Resource, and proceed like the first example which works. Seems redundant, but if you can get the file, then you can test a number of things:
Write the file to disk and check the content
Write the file to disk and compare the sizes, etc.
import java.io.File;
import java.io.FileInputStream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
#Value(value = "classpath:images/account.png")
private Resource defaultAccountImage;
private byte[] getByteArrayFromFile(Resource image) {
FileInputStream fileInputStream = null;
byte[] bFile = null;
try {
File file = image.getFile();
bFile = new byte[(int) file.length()];
// convert file into array of bytes
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
for (int i = 0; i < bFile.length; i++) {
System.out.print((char) bFile[i]);
}
System.out.println("Done");
} catch (Exception e) {
e.printStackTrace();
}
return bFile;
}

Fetch image from Rich Text Field via Https

I have C# batch which communicate with salesforce objects via SOAP API.
I want to fetch an image which is part of the Rich Text Field in salesforce custom object.
The rich text field himself is Html text and I can get the url of the image tag, but the problem is that the url himself is Https = Http secure connection:
The url of the image tag:
https://ngam--kerensen--c.cs17.content.force.com/servlet/rtaImage?eid=a1Vg0000000lTkK&feoid=00N20000003jcie&refid=0EMg00000009N4I
As a result of this, I can't get the resource and write it to a file as a local image for later use.
This is the C# code for fetching the image:
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
bool answer = response.ContentType.StartsWith("image", StringComparison.OrdinalIgnoreCase);
if ((response.StatusCode == HttpStatusCode.OK ||
response.StatusCode == HttpStatusCode.Moved ||
response.StatusCode == HttpStatusCode.Redirect)&&
response.ContentType.StartsWith("text/html", StringComparison.OrdinalIgnoreCase)){
// if the remote file was found, download it
using (Stream inputStream = response.GetResponseStream())
using (Stream outputStream = File.OpenWrite(filepath))
{
byte[] buffer = new byte[8192];
int bytesRead;
logger.InfoFormat("Writing to file, filepath:{0}", filepath);
do
{
bytesRead = inputStream.Read(buffer, 0, buffer.Length);
logger.InfoFormat("bytesRead: {0} ", bytesRead);
outputStream.Write(buffer, 0, bytesRead);
} while (bytesRead != 0);
//TODO outputStream.Close();
}
return true;
}
}
catch (Exception ex)
{
logger.ErrorFormat("Failed to download {0} to {1} : {2}",
uri, filepath, ex.Message);
logger.Debug("Failed to download " + uri, ex);
}
return false;
I know that the secure url using java servlet for exposing the image to the world.
The problem is that I can't get the image data and always get the following respond:
<script>
if (this.SfdcApp && this.SfdcApp.projectOneNavigator) { SfdcApp.projectOneNavigator.handleRedirect('https://ngam--kerensen.cs17.my.salesforce.com?ec=302&startURL=%2Fcontent%2Fsession%3Furl%3Dhttps%253A%252F%252Fngam--kerensen--c.cs17.content.force.com%252Fservlet%252FrtaImage%253Feid%253Da1Vg0000000lTkK%2526feoid%253D00N20000003jcie%2526refid%253D0EMg00000009N4I'); } else
if (window.location.replace){
window.location.replace('https://ngam--kerensen.cs17.my.salesforce.com?ec=302&startURL=%2Fcontent%2Fsession%3Furl%3Dhttps%253A%252F%252Fngam--kerensen--c.cs17.content.force.com%252Fservlet%252FrtaImage%253Feid%253Da1Vg0000000lTkK%2526feoid%253D00N20000003jcie%2526refid%253D0EMg00000009N4I');
} else {;
window.location.href ='https://ngam--kerensen.cs17.my.salesforce.com?ec=302&startURL=%2Fcontent%2Fsession%3Furl%3Dhttps%253A%252F%252Fngam--kerensen--c.cs17.content.force.com%252Fservlet%252FrtaImage%253Feid%253Da1Vg0000000lTkK%2526feoid%253D00N20000003jcie%2526refid%253D0EMg00000009N4I';
}
</script>
Any suggestion how to get the actual image data from the Rich Text Area field.

Route HTTP images for a HTTPS site

I'm running a site where users can link images and thumbnails from other sites in their content. When viewing these images in the https secured user are, they are getting security warnings, because http content is contained in the https page..
To work around this, I'd like to route the images through my server to the client, there by "giving" them the required https protokoll.
e.g. when viewing content on the secure site an image tag would like this:
<img src="https://mysite/img.aspx?src=http://url.to/someimage.jpg" >
As my site using Umbraco (.NET 3.5, IIS7), I've already looked into using the urlrewritingnet library, but it only seems to be able to rewrite and redirect urls.
Has anybody done this?
The following works quite well:
I've got it to work by just passsing through the image bytes on the server. I'm not entierly convinced that it is a good solution so, I'll wait for better solutions:
public partial class Img : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string url = Page.Request.QueryString["url"];
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
request.Timeout = 5000;
request.ReadWriteTimeout = 20000;
HttpWebResponse imgresponse = (HttpWebResponse)request.GetResponse();
//StreamReader sr = new StreamReader(imgresponse.GetResponseStream());
Response.ContentType = "image/gif";
byte[] fileBytes = GetFileBytes(imgresponse.GetResponseStream());
Response.BinaryWrite(fileBytes);
Response.Flush();
}
protected byte[] GetFileBytes(Stream stream)
{
byte[] fileBytes = null;
byte[] buffer = new byte[4096];
try
{
MemoryStream memoryStream = new MemoryStream();
int chunkSize = 0;
do
{
chunkSize = stream.Read(buffer, 0, buffer.Length);
memoryStream.Write(buffer, 0, chunkSize);
} while (chunkSize != 0);
fileBytes = memoryStream.ToArray();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
return fileBytes;
}
}

Convert byte[] to Base64 string for data URI

I know this has probably been asked 10000 times, however, I can't seem to find a straight answer to the question.
I have a LOB stored in my db that represents an image; I am getting that image from the DB and I would like to show it on a web page via the HTML IMG tag. This isn't my preferred solution, but it's a stop-gap implementation until I can find a better solution.
I'm trying to convert the byte[] to Base64 using the Apache Commons Codec in the following way:
String base64String = Base64.encodeBase64String({my byte[]});
Then, I am trying to show my image on my page like this:
<img src="data:image/jpg;base64,{base64String from above}"/>
It's displaying the browser's default "I cannot find this image", image.
Does anyone have any ideas?
Thanks.
I used this and it worked fine (contrary to the accepted answer, which uses a format not recommended for this scenario):
StringBuilder sb = new StringBuilder();
sb.append("data:image/png;base64,");
sb.append(StringUtils.newStringUtf8(Base64.encodeBase64(imageByteArray, false)));
contourChart = sb.toString();
According to the official documentation Base64.encodeBase64URLSafeString(byte[] binaryData) should be what you're looking for.
Also mime type for JPG is image/jpeg.
That's the correct syntax. It might be that your web browser does not support the data URI scheme. See Which browsers support data URIs and since which version?
Also, the JPEG MIME type is image/jpeg.
You may also want to consider streaming the images out to the browser rather than encoding them on the page itself.
Here's an example of streaming an image contained in a file out to the browser via a servlet, which could easily be adopted to stream the contents of your BLOB, rather than a file:
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
ServletOutputStream sos = resp.getOutputStream();
try {
final String someImageName = req.getParameter(someKey);
// encode the image path and write the resulting path to the response
File imgFile = new File(someImageName);
writeResponse(resp, sos, imgFile);
}
catch (URISyntaxException e) {
throw new ServletException(e);
}
finally {
sos.close();
}
}
private void writeResponse(HttpServletResponse resp, OutputStream out, File file)
throws URISyntaxException, FileNotFoundException, IOException
{
// Get the MIME type of the file
String mimeType = getServletContext().getMimeType(file.getAbsolutePath());
if (mimeType == null) {
log.warn("Could not get MIME type of file: " + file.getAbsolutePath());
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
return;
}
resp.setContentType(mimeType);
resp.setContentLength((int)file.length());
writeToFile(out, file);
}
private void writeToFile(OutputStream out, File file)
throws FileNotFoundException, IOException
{
final int BUF_SIZE = 8192;
// write the contents of the file to the output stream
FileInputStream in = new FileInputStream(file);
try {
byte[] buf = new byte[BUF_SIZE];
for (int count = 0; (count = in.read(buf)) >= 0;) {
out.write(buf, 0, count);
}
}
finally {
in.close();
}
}
If you don't want to stream from a servlet, then save the file to a directory in the webroot and then create the src pointing to that location. That way the web server does the work of serving the file. If you are feeling particularly clever, you can check for an existing file by timestamp/inode/crc32 and only write it out if it has changed in the DB which can give you a performance boost. This file method also will automatically support ETag and if-modified-since headers so that the browser can cache the file properly.

Categories