Streaming a multipart in Jersey 2 - java

I currently have Jersey REST code to stream a single file which works great:
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream out)
throws IOException {
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);
// Stream is filled with data in this method.
restDAO.readData(bufferedOutputStream);
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
};
return Response.ok(body, mimeType).header("filename", getFileName()).build();
However, I was wanting to stream a multipart file which contains both a large file and JSON, doing something like this:
FormDataMultiPart multiPart = new FormDataMultiPart();
multiPart.bodyPart(jsonObject, MediaType.APPLICATION_JSON_TYPE);
String mimeType = "application/octet-stream";
StreamingOutput stream = new StreamingOutput() {
#Override
public void write(OutputStream out)
throws IOException {
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(out);
// Stream is filled with data in this method.
restDAO.readData(bufferedOutputStream);
bufferedOutputStream.flush();
bufferedOutputStream.close();
}
};
multiPart.bodyPart(stream, MediaTypeUtil.stringToMediaType(mimeType));
return Response.ok(multiPart, MediaType.MULTIPART_FORM_DATA).build();
However, the above code does not work. I get this error when running: javax.ws.rs.BadRequestException: HTTP 400 Bad Request
Is it possible to stream a multiPart in a similar manner? The main problem I see is that the file going into the multipart is coming from a stream itself.

To properly stream the multipart, I ended up using PipedInputStream and PipedOutputStream along with a thread:
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(pipedOutputStream);
FormDataMultiPart multiPart = new FormDataMultiPart();
multiPart.bodyPart(jsonObject, MediaType.APPLICATION_JSON_TYPE);
String mimeType = "application/octet-stream";
// Multipart streaming.
// Write to the PipedOutputStream in a separate thread
Runnable runnable = new Runnable() {
public void run() {
try {
restDAO.readData(bufferedOutputStream);
bufferedOutputStream.flush();
bufferedOutputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
};
Thread fileThread = new Thread(runnable, "MultiPartFileStreamer");
fileThread.start();
final StreamDataBodyPart streamDataBodyPart = new StreamDataBodyPart(
"file", pipedInputStream, data.getContentFileName(),
MediaUtils.stringToMediaType(mimeType));
multiPart.bodyPart(streamDataBodyPart);
return Response.ok(multiPart, MediaType.MULTIPART_FORM_DATA).build();

Related

How get instance of HttpServletResponse for download files with JAVA EE

I have a file txt on the server (previously generated). When user clicks on button it generates the file, now I want (additionally) download the file inside my function. But I can't make it work(I'm new on JAVA EE), cause I don't know how to get HttpServletResponse.
From web I call function with this:
#Path("getreport")
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public JSONObject getreport(CommonInput input) {
JSONObject j = objectmapper.conertValue(reportBean.getreport(),JSONObject.class);
return j;
}
reprotBean has function:
public void getreport() {
//...doing many things
//generating my file
List<String> lines = new ArrayList<>();
lines.add("star file");
//..adding many lines
Path file = Paths.get("C:\\Users\\myuser\\file.txt");
Files.write(file, lines, StandardCharsets.UTF_8);
downloadFile();
//...doing many things
}
I found this way to download my file:
public void downloadFile(HttpServletResponse response){
String sourceFile = ""C:\\Users\\myuser\\file.txt"";
try {
FileInputStream inputStream = new FileInputStream(sourceFile);
String disposition = "attachment; fileName=outputfile.txt";
response.setContentType("text/txt");
response.setHeader("Content-Disposition", disposition);
response.setHeader("content-Length", String.valueOf(stream(inputStream, response.getOutputStream())));
} catch (IOException e) {
logger.error("Error occurred while downloading file {}",e);
}
}
private long stream(InputStream input, OutputStream output) throws IOException {
try (ReadableByteChannel inputChannel = Channels.newChannel(input); WritableByteChannel outputChannel = Channels.newChannel(output)) {
ByteBuffer buffer = ByteBuffer.allocate(10240);
long size = 0;
while (inputChannel.read(buffer) != -1) {
buffer.flip();
size += outputChannel.write(buffer);
buffer.clear();
}
return size;
}
}
When I try to use downloadFile(), it requires HttpServletResponse, and I don't have that parameter. I can't understand how to get that (how it works), or do I have to use another method for download my file?
All solutions I found requires HttpServletResponse (download files from browsers)
If you have that file generated already. Just need write it to HttpServletResponse
resp.setContentType("text/plain");
resp.setHeader("Content-disposition", "attachment; filename=sample.txt");
try(InputStream in = req.getServletContext().getResourceAsStream("sample.txt");
OutputStream out = resp.getOutputStream()) {
byte[] buffer = new byte[ARBITARY_SIZE];
int numBytesRead;
while ((numBytesRead = in.read(buffer)) > 0) {
out.write(buffer, 0, numBytesRead);
}
}
Be sure to make your file to be accessed by ServeletContext
If you are using Spring Rest framework. Can refer to below
#GetMapping("/download")
public ResponseEntity<byte[]> downloadErrorData() throws Exception {
List<Employee> employees = employeeService.getEmployees();
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(employees);
byte[] isr = json.getBytes();
String fileName = "employees.json";
HttpHeaders respHeaders = new HttpHeaders();
respHeaders.setContentLength(isr.length);
respHeaders.setContentType(new MediaType("text", "json"));
respHeaders.setCacheControl("must-revalidate, post-check=0, pre-check=0");
respHeaders.set(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName);
return new ResponseEntity<byte[]>(isr, respHeaders, HttpStatus.OK);
}
credit to: https://www.jeejava.com/file-download-example-using-spring-rest-controller/

Java Convert byte array to PDF returns "undefined"

I'm trying to convert a byte array to a PDF document, but the the PDF file seems to be corrupted. And if open the file with a text reader the file just say "Undefined" I have searched through various stack topics but no luck. And the way i do it should work according to other topics. Below is my code. The code is executed trough a rest controller. Would really appreciate if someone could help me :).
//Controller
#ResponseBody
#RequestMapping(value = "/orders/{orderCode}/receipt", method = RequestMethod.GET, produces = "application/pdf")
public void getOrderReceiptConroller(#PathVariable("orderCode") final String orderCode, final HttpServletResponse response)
{
response.setContentType(CoreConstants.MIME_TYPE_APPLICATION_PDF);
final InputStream inputStream = new ByteArrayInputStream(OrderFacade.getReceiptPdf(orderCode));
OutputStream outputStream = null;
try
{
outputStream = response.getOutputStream();
IOUtils.copy(inputStream, outputStream);
}
catch (final IOException e)
{
}
finally
{
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
// calls this code that returns the byteArray.
private byte[] getReceiptPdf()
{
Gson gson = new Gson();
Edwresults edwResult = gson.fromJson(new FileReader(mockResponsePath), Edwresults.class);
String response = edwResult.getResults().get(0).getData().get(0).getDigitalReceipt();
byte[] byteData = response.getBytes();
return byteData;
}

JAVA AWS S3 multipart upload with inputstream and length of inputstream is unknown

If content length is unknown then AmazonS3Client and TransferManager buffers the content in the memory,it results in out of memory exception.
So i wanted to go with multipart upload(Low Level of API).
But i didn't find ways to upload with inputstream(Examples are there with File).
I tried by passing inputstream but no result.
My Code :: (Inside Vaadin upload receiver)
#Override
public OutputStream receiveUpload(final String filename, String mimeType) {
this.fileName=filename;
PipedOutputStream pos=null;
try {
pos= new PipedOutputStream();
final InputStream is = new PipedInputStream(pos);
new Thread(){
#Override
public void run(){
try{
List<PartETag> partETags = new ArrayList<PartETag>();
InitiateMultipartUploadRequest initRequest = new InitiateMultipartUploadRequest(
bucketName, filename);
InitiateMultipartUploadResult initResponse =
s3.initiateMultipartUpload(initRequest);
long partSize = 5 * 1024 * 1024; // Set part size to 5 MB.
int size = 1024;
byte[] buffer = new byte[size];
int len=0;
int partNum = 1;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
for(int i=1;(len=is.read(buffer,0,size))!=-1;i++){
bos.write(buffer,0,len);
if(bos.size()>=partSize){
UploadPartRequest uploadRequest = new UploadPartRequest()
.withBucketName(bucketName).withKey(filename)
.withUploadId(initResponse.getUploadId()).withPartNumber(partNum++)
.withInputStream(is)
.withPartSize(partSize);
partETags.add(
s3.uploadPart(uploadRequest).getPartETag());
bos.flush();
bos.close();
ins.close();
bos = new ByteArrayOutputStream();
}
}
//rest of data, written to s3
System.out.println("After remaining bos "+bos.size());
UploadPartRequest uploadRequest = new UploadPartRequest()
.withBucketName(bucketName).withKey(filename)
.withUploadId(initResponse.getUploadId()).withPartNumber(partNum++)
.withInputStream(is)
.withLastPart(true);
partETags.add(
s3.uploadPart(uploadRequest).getPartETag());
CompleteMultipartUploadRequest compRequest = new
CompleteMultipartUploadRequest(
bucketName,
filename,
initResponse.getUploadId(),
partETags);
s3.completeMultipartUpload(compRequest);
}
}.start();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return pos;
}
.withInputStream(is)
here I'm passing original stream or do i need to create a temp stream
with read content.
Thanks in advance.

Audio file stream in HTTP Post Jersey using Java

I have been stuck on an issue for the last couple of days and frankly I am out of ideas. What I am trying to do is to host an rest service using Jersey that will accept a post request with stream of audio data in its payload.
Issue I have been running into is that that I loose data consistency (I am running CRC check on both client and server) and after 8 reads of with 1024 bytes buffer CRC between data sent and data received becomes inconsistent. It works just fine when I am dealing with text or content of smaller size. Code is attached, can anyone please tell me what I am doing wrong?
Server:
#POST
#Consumes("audio/wav")
#Produces(MediaType.TEXT_PLAIN)
public String streamCommand(#Context HttpServletRequest request ) throws Exception
{
CRC32 crc = new CRC32();
InputStream stream = request.getInputStream();
byte[] readBuffer = new byte[1024];
StringBuilder builder = new StringBuilder();
while (stream.read(readBuffer) > -1)
{
crc.update(readBuffer);
builder.append(new String(readBuffer));
System.out.println(crc.getValue());
}
return builder.toString();
}
Client:
static final String SOUND_FILE_NAME = "SoundTest.wav";
#BeforeClass
public static void setup() throws Exception
{
soundStream = classloader.getResourceAsStream(SOUND_FILE_NAME);
}
#Test
public void test() throws Exception {
PipedOutputStream stream = new PipedOutputStream();
DataStreamer data = new DataStreamer(stream, soundStream);
ExecutorService executor = Executors.newFixedThreadPool(1);
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("http://localhost:8080/EVAFrontEnd/webapi/users/1/devices/1/command");
post.addHeader(HttpHeaders.CONTENT_TYPE, "audio/wav");
InputStreamEntity requestEntity = new InputStreamEntity(new PipedInputStream((PipedOutputStream) stream), -1);
post.setEntity(requestEntity);
executor.execute(data);
executor.shutdown();
HttpResponse r = client.execute(post);
assertNotNull(r);
}
Data Streamer:
public class DataStreamer implements Runnable {
OutputStream writeStream;
CheckedInputStream readStream;
static Logger logger = Logger.getLogger(DataStreamer.class);
public DataStreamer(OutputStream stream, InputStream readingStrem) {
this.writeStream = stream;
this.readStream = new CheckedInputStream(readingStrem, new Adler32());
}
#Override
public void run()
{
CRC32 crc = new CRC32();
try {
byte[] buffer = new byte[1024];
while (readStream.read(buffer) > -1) {
crc.update(buffer);
System.out.println(crc.getValue());
writeStream.write(buffer);
}
System.out.println("END CRC");
readStream.close();
writeStream.close();
}
catch (Exception e) {
logger.error("Unable to stream data.", e);
}
}
}
Thank you!
You need to always save the number of bytes written by
bytes_read = readStream.read(buffer);
into a variable, because that method sometimes gives a short result. Then use ArrayList.copyOfRange to create a subarray [0 ... bytes_read-1] from the buffer with only the valid bytes.

Upload file via streaming using Jersey 2

I am trying to create a file upload API using Jersey. I would like to obtain details about the upload progress in the server side (is it possible?). Searching the web, the suggestion was to use stream to transfer the file. But... even was described below, the server just to execute the "putFile" method after the file arrives completely. Another problem is that these code only works to small files, when I try a file greater than 40mb
#Path("/file")
public class LargeUpload {
private static final String SERVER_UPLOAD_LOCATION_FOLDER = "/Users/diego/Documents/uploads/";
#PUT
#Path("/upload/{attachmentName}")
#Consumes(MediaType.APPLICATION_OCTET_STREAM)
public Response putFile(#PathParam("attachmentName") String attachmentName,
InputStream fileInputStream) throws Throwable {
String filePath = SERVER_UPLOAD_LOCATION_FOLDER + attachmentName;
saveFile(fileInputStream, filePath);
String output = "File saved to server location : ";
return Response.status(200).entity(output).build();
}
// save uploaded file to a defined location on the server
private void saveFile(InputStream uploadedInputStream, String serverLocation) {
try {
OutputStream outpuStream = new FileOutputStream(new File(
serverLocation));
int read = 0;
byte[] bytes = new byte[1024];
outpuStream = new FileOutputStream(new File(serverLocation));
while ((read = uploadedInputStream.read(bytes)) != -1) {
outpuStream.write(bytes, 0, read);
}
outpuStream.flush();
outpuStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws FileNotFoundException {
ClientConfig config = new ClientConfig();
config.property(ClientProperties.CHUNKED_ENCODING_SIZE, 1024);
Client client = ClientBuilder.newClient(config);
File fileName = new File("/Users/diego/Movies/ff.mp4");
InputStream fileInStream = new FileInputStream(fileName);
String sContentDisposition = "attachment; filename=\"" + fileName.getName()+"\"";
Response response = client.target("http://localhost:8080").path("upload-controller/webapi/file/upload/"+fileName.getName()).
request(MediaType.APPLICATION_OCTET_STREAM).header("Content-Disposition", sContentDisposition).
put(Entity.entity(fileInStream, MediaType.APPLICATION_OCTET_STREAM));
System.out.println(response);
}

Categories

Resources