I want to expose an API to download a S3 bucket file content as stream to its consumers.
The API URL is like /downloadfile/** which is GET request.
What should be my return type right now I tried with accept header=
application/octet-stream which didn't work.
I don't want to write the content of file to any file and send it. It should be returned as a stream that's it.
Here is the controller pseudo code I wrote till now which is giving me 406 error all the time.
#GetMapping(value = "/downloadfile/**", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<Object> downloadFile(HttpServletRequest request) {
//reads the content from S3 bucket and returns a S3ObjectInputStream
S3ObjectInputStream object = null;
object = publishAmazonS3.getObject("12345bucket", "/logs/file1.log").getObjectContent();
return object
}
Any suggestions here on the way of doing this and what I am doing wrong?
I was able to download the file as a stream by using StreamingResponseBody class from Spring.
Here is the code I used:
#GetMapping(value = "/downloadfile/**", produces = { MediaType.APPLICATION_OCTET_STREAM_VALUE })
public ResponseEntity<StreamingResponseBody> downloadFile(HttpServletRequest request) {
//reads the content from S3 bucket and returns a S3ObjectInputStream
S3Object object = publishAmazonS3.getObject("12345bucket", "/logs/file1.log");
S3ObjectInputStream finalObject = object.getObjectContent();
final StreamingResponseBody body = outputStream -> {
int numberOfBytesToWrite = 0;
byte[] data = new byte[1024];
while ((numberOfBytesToWrite = finalObject.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes..");
outputStream.write(data, 0, numberOfBytesToWrite);
}
finalObject.close();
};
return new ResponseEntity<>(body, HttpStatus.OK);
}
The way to test the streaming is done correctly or not is to have a file of around 400mb to download. Reduce your Xmx to 256mb by passing the in the vm options. Now, compare the download functionality with and without using StreamingResponseBody, you will get OutofMemoryError when using the conventional OutputStreams for writing the content
You can solve with the below example
import java.io.ByteArrayOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import com.grokonez.s3.services.S3Services;
#RestController
public class DownloadFileController {
#Autowired
S3Services s3Services;
/*
* Download Files
*/
#GetMapping("/api/file/{keyname}")
public ResponseEntity<byte[]> downloadFile(#PathVariable String keyname) {
ByteArrayOutputStream downloadInputStream = s3Services.downloadFile(keyname);
return ResponseEntity.ok().contentType(contentType(keyname))
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + keyname + "\"")
.body(downloadInputStream.toByteArray());
}
private MediaType contentType(String keyname) {
String[] arr = keyname.split("\\.");
String type = arr[arr.length - 1];
switch (type) {
case "txt":
return MediaType.TEXT_PLAIN;
case "png":
return MediaType.IMAGE_PNG;
case "jpg":
return MediaType.IMAGE_JPEG;
default:
return MediaType.APPLICATION_OCTET_STREAM;
}
}
}
Related
I am trying to return a list of files from a directory. Here's my code:
package com.demo.web.api.file;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.demo.core.Logger;
import io.swagger.v3.oas.annotations.Operation;
#RestController
#RequestMapping(value = "/files")
public class FileService {
private static final Logger logger = Logger.factory(FileService.class);
#Value("${file-upload-path}")
public String DIRECTORY;
#Value("${file-upload-check-subfolders}")
public boolean CHECK_SUBFOLDERS;
#GetMapping(value = "/list")
#Operation(summary = "Get list of Uploaded files")
public ResponseEntity<List<File>> list() {
List<File> files = new ArrayList<>();
if (CHECK_SUBFOLDERS) {
// Recursive check
try (Stream<Path> walk = Files.walk(Paths.get(DIRECTORY))) {
List<Path> result = walk.filter(Files::isRegularFile).collect(Collectors.toList());
for (Path p : result) {
files.add(p.toFile().getAbsoluteFile());
}
} catch (Exception e) {
logger.error(e.getMessage());
}
} else {
// Checks the root directory only.
try (Stream<Path> walk = Files.walk(Paths.get(DIRECTORY), 1)) {
List<Path> result = walk.filter(Files::isRegularFile).collect(Collectors.toList());
for (Path p : result) {
files.add(p.toFile().getAbsoluteFile());
}
} catch (Exception e) {
logger.error(e.getMessage());
}
}
return ResponseEntity.ok().body(files);
}
}
As seen in the code, I am trying to return a list of files.
However, when I test in PostMan, I get a list of string instead.
How can I make it return the file object instead of the file path string? I need to get the file attributes (size, date, etc.) to display in my view.
I would recommend that you change your ResponseEntity<> to return not a List of File but instead, a List of Resource, which you can then use to obtain the file metadata that you need.
public ResponseEntity<List<Resource>> list() {}
You can also try specifying a produces=MediaType... param in your #GetMapping annotation so as to tell the HTTP marshaller which kind of content to expect.
You'd have to Create a separate payload with the details you wanna respond with.
public class FilePayload{
private String id;
private String name;
private String size;
public static fromFile(File file){
// create FilePayload from File object here
}
}
And convert it using a mapper from your internal DTO objects to payload ones.
final List<FilePayload> payload = files.forEach(FilePayload::fromFile).collect(Collectors.toList());
return new ResponseEntity<>(payload, HttpStatus.OK);
I think you should not return a body in this case as you may be unaware of the size.
Better to have another endpoint to GET /files/{id}
I did give this another thought. What I just needed was the filename, size and date of the file. From there, I can get the file extension and make my list display look good already.
Here's the refactored method:
#GetMapping(value = "/list")
#Operation(summary = "Get list of Uploaded files")
public ResponseEntity<String> list() {
JSONObject responseObj = new JSONObject();
List<JSONObject> files = new ArrayList<>();
// If CHECK_SUBFOLDERS is true, pass MAX_VALUE to make it recursive on all
// sub-folders. Otherwise, pass 1 to use the root directory only.
try (Stream<Path> walk = Files.walk(Paths.get(DIRECTORY), CHECK_SUBFOLDERS ? MAX_VALUE : 1)) {
List<Path> result = walk.filter(Files::isRegularFile).collect(Collectors.toList());
for (Path p : result) {
JSONObject file = new JSONObject();
file.put("name", p.toFile().getName());
file.put("size", p.toFile().length());
file.put("lastModified", p.toFile().lastModified());
files.add(file);
}
responseObj.put("data", files);
} catch (Exception e) {
String errMsg = CoreUtils.formatString("%s: Error reading files from the directory: \"%s\"",
e.getClass().getName(), DIRECTORY);
logger.error(e, errMsg);
responseObj.put("errors", errMsg);
}
return ResponseEntity.ok().body(responseObj.toString());
}
The above was what I ended up doing. I created a JSONObject of the props I need, and then returned the error if it did not succeed. This made it a lot better for me.
I have a situation in which I need to transmit an object from back-end to front-end in this format:
{
filename: "filename",
type: "type",
src: "src",
bytes: "base64Representation"
}
The bytes property of the object consists in the base64 representation of a file stored in a repository in the remote server. Up until now I've worked with small files in the range 1-2MB and the code for converting a file to the corresponding base64 representation has worked correctly. But now I'm facing some problems with big files, larger than 100MB. I've checked solutions that try to convert the file chunk by chunk, but still at the end of the process I need all the chunks concatenated in a string and at this step I'm getting an OutOfMemory exception. I've also seen some suggestions to use OutputStreams, but I can't apply them because I need the data in the above format. Please does anyone have any suggestions on how can I bypass this situation?
You can use OutputStream and process on the fly in a servlet by wrapping response.getOutputStream(). I will give a working example with spring boot. I tested and it works.
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Base64;
#RestController
public class Base64Controller {
#RequestMapping(value = "/base64", method = RequestMethod.GET)
public void getBase64File(HttpServletResponse response) throws IOException {
response.setContentType("text/plain");
OutputStream wrap = Base64.getEncoder().wrap(response.getOutputStream());
FileInputStream fis = new FileInputStream("./temp.txt");
int bytes;
byte[] buffer = new byte[2048];
while ((bytes=fis.read(buffer)) != -1) {
wrap.write(buffer, 0, bytes);
}
fis.close();
wrap.close();
}
}
A JSON response is a kludge here, with Base64 having a payload of 6/8th per byte, you have 33% more data transfer as needed. Indeed a JSON DOM object is overstretching both the server as also the client side.
So convert it to a simple binary download, and stream it out; possibly throttled for large data.
This means a change in the API.
I never worked with struts, so i'm not sure will this work, but it should be something like that
public class DownloadB64Action extends Action{
private final static BUFFER_SIZE = 1024;
#Override
public ActionForward execute(ActionMapping mapping, ActionForm form,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
response.setContentType("text/plain");
try
{
FileInputStream in =
new FileInputStream(new File("myfile.b64"));
ServletOutputStream out = Base64.getEncoder().wrap(response.getOutputStream());
byte[] buffer = new byte[BUFFER_SIZE];
while(in.read(buffer, 0, BUFFER_SIZE) != -1){
out.write(buffer, 0, BUFFER_SIZE);
}
in.close();
out.flush();
out.close();
}catch(Exception e){
//TODO handle exception
}
return null;
}
}
to make it JSON structure like you need, you might try to write directly to response.getOutputStream() "{\"filename\":\"filename\",\"type\":\"type\",\"src\":\"src\",\"bytes\": \"".getBytes() before b64 payload and "\"}".getBytes() after
}
Given that it is now possible to handle binary data in Amazon Api Gateway and Amazon Lambda, I wanted to try to make an Amazon Lambda endpoint which returned an Excel spreadsheet. It is entirely possible to do so using node/js, as demonstrated here. Unfortunately, any time I try to do this using Java, it falls to pieces.
My initial attempt was to create a simple workbook using apache XSSFWorkbook, write it to the output stream provided by RequestStreamHandler, and done.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class FileRequestHandler implements RequestStreamHandler {
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
Workbook wb = new XSSFWorkbook();
String sheetName = "Problem sheet";
wb.createSheet(sheetName);
wb.write(outputStream);
}
}
When tested locally, the output stream can be piped to a file resulting in a valid output excel file.
import com.amazonaws.util.StringInputStream;
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class FileRequestHandlerTest {
#Test
public void shouldCreateExcelFile() throws IOException {
FileRequestHandler fileRequestHandler = new FileRequestHandler();
InputStream inputStream = new StringInputStream("hello world");
String fileName = "FileRequestLambda";
String path = fileName + ".xlsx";
FileOutputStream fileOutputStream = new FileOutputStream(path);
fileRequestHandler.handleRequest(inputStream, fileOutputStream, TestUtils.createContext());
fileOutputStream.close();
}
}
But when I run it in Amazon Lambda, I get malformed binary output:
PKn��I_rels/.rels���j�0��}
�{㴃1F�^Ơ�2��l%1I,c�[��3�l
l�����H��4��R�l��·����q}*�2�������;�*��
t"�^�l;1W)�N�iD)ejuD�cKz[:}g����#:�
�3����4�7N�s_ni�G�M*7�����2R�+�� �2�/�����b��mC�Pp�ֱ$POyQ�抒�DsZ��IС�'un���~�PK����OPKn��I[Content_Types].xml�SMO1��+6��m��1���G%��β
�J[���MDL0�S;yo�{3i�Ӎ5�c��5lć�B'��nѰ��S}˪��)0�aÜg��`<�L��.�p'D�ZH�t��>Z�Tƅ ��#q=��]F��\4�=`+���P�!-!S.�v�#��+�����N�tEV=nHe7���S,;K]_h7Q+�W8߶Z��re��c�U�����}�����g�&A��,���H�$�B<��`�"�Jb���"���I�N�1���A���CI�#��܂v��?|\�{��`�b������$�c�D��|2�PKKB�>'PKn��IdocProps/app.xmlM��
�0D�~EȽ��ADҔ���A? ��6�lB�J?ߜ���0���ͯ��)�#��H6���V>��$;�SC
;̢(�ra�g�l�&�e��L!y�%��49��`_���4G���F��J��Wg
�GS�b����
~�PK�|wؑ�PKn��IdocProps/core.xmlm��J�0F��!�m�V����(���Ż��m��!�v}{ӺVP/g��a��wG5�wp~4��4�1-�u���n��c�גOFC����6��e�888c��<�홰
B��/P�g��q�b��!��'��W�)��"
�<p�S��I)Ŧ�onZR�#��Ќ�6�S�߅u��G?n�<��\�\����ۛ���t���p|��f� Q4��ac&ߓ��������i��"�UG+vV��z�ɯ���U�^�H#�����IM�$�&�PK����PKn��Ixl/sharedStrings.xml=�A� ツ��.z0Ɣ�`������,�����q2��o�ԇ���N�E��x5�z>�W���(R�K���^4{�����ŀ�5��y�V����y�m�XV�\�.��j�����
8�PKp��&x�PKn��I
xl/styles.xml���n� ��>bop2TQ��P)U��RWb�6*�����ӤS�Nw�s���3ߍ���t��(l��������ҝx�!N=#$ɀ��}��3c���ʰr`:i��2��w,�
�d
�T��R#�voc �;c�iE���Û��E<|��4Iɣ�����F#��n���B�z�F���y�j3y��yҥ�jt>���2��Lژ�!6��2F��OY��4#M�!���G��������1�t��y��p��" n����u�����a�ΦDi�9�&#��%I��9��}���cK��T��$?������`J������7���o��f��M|PK�1X#C�PKn��Ixl/workbook.xml���N�0��<��wj�E�8��J��P�;�����hmZ'Q�#����~;���;vCJ6 �Fà���"��|x|�}���#]����C�0�<֜'=�WiG��#y���O#�2i#������+`!��F�{��-�O�!/B�r)�;&h�����zOz�o����xO��I2����YuĔ��s�u��<J8Q�z6��Qm�:�,�c��Z�����PK1����dPKn��Ixl/_rels/workbook.xml.rels��Mk1#���0�nv-�R�^����0$����$dƯo���R�OC�ރ�-��������#Sՠ(�����ܼ?��b��p�����d�AJ�¾O�
#�/�f�iD�b�P6m�#Jy�N'�[�HO��E�k����3�W���ܑ`���Zri㪐����?�ض��e�������7p�wj�W5r���]������=�|���<:�[p��7�O�PK��4��9PKn��Ixl/worksheets/sheet1.xmleP�N�0���މ�V��THU$���$��j���[��c�����3��-v�nT���/a����7�Zߗ��z���]uQ���0 ��zJD�[�C3�3!� }|鈝�H��ab4�br�^���v�z���:�)P1v%ܭ#W�"|�8�?X�ܚ���C[B�'�~��ȅO������Tyb�bgN�<�|��$��ƙ��{#&����h��>��D�Ű�z�#��6��8�LF�dQ����,4�xS����/PK�_�Y�lPKn��I����O_rels/.relsPKn��IKB�>'[Content_Types].xmlPKn��I�|wؑ��docProps/app.xmlPKn��I����mdocProps/core.xmlPKn��Ip��&x��xl/sharedStrings.xmlPKn��I�1X#C�
nxl/styles.xmlPKn��I1����d�xl/workbook.xmlPKn��I��4��9xl/_rels/workbook.xml.relsPKn��I�_�Y�l$ xl/worksheets/sheet1.xmlPK ?Z
The output is about 5KB in size, while the output on my local computer is about 3KB in size. This appears to be a problem with binary output in general for Java on Amazon Lambda. When I do run some code that writes an image to the output string, it also works locally, but results in an image twice the size and garbled when run from Amazon Lambda.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
import java.io.*;
import java.net.URL;
public class ImageRequestHandler implements RequestStreamHandler {
public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
throws IOException {
String address = "https://upload.wikimedia.org/wikipedia/commons/thumb/1/1d/AmazonWebservices_Logo.svg/580px-AmazonWebservices_Logo.svg.png";
URL url = new URL(address);
InputStream in = new BufferedInputStream(url.openStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int n;
while (-1!=(n=in.read(buf)))
{
out.write(buf, 0, n);
}
out.close();
in.close();
byte[] response = out.toByteArray();
outputStream.write(response);
}
}
The types of the was input and output streams are:
lambdainternal.util.NativeMemoryAsInputStream
lambdainternal.util.LambdaByteArrayOutputStream
Help?
I had the same problem with returning JPG image from Amazon Lambda and I found a work-around.
You need to encode an output stream with base64 encoding:
OutputStream encodedStream = Base64.getEncoder().wrap(outputStream);
encodedStream.write(response);
encodedStream.close();
Then you need to update Method Response and Integration Response of your function as described here: AWS Gateway API base64Decode produces garbled binary?
I upload an audio file to an audio & video bucket, called demo, using the AcrCloud RESTful services. I am getting a 500 Internal Server Error. This indicates that my signature is correct (I was getting a 422 when the signature was incorrect). The part that I suspect is incorrect is the construction of the multipart post request
My Code:
import com.xperiel.common.logging.Loggers;
import com.google.api.client.http.ByteArrayContent;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpContent;
import com.google.api.client.http.HttpHeaders;
import com.google.api.client.http.HttpMediaType;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.MultipartContent;
import com.google.api.client.http.MultipartContent.Part;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;
import com.google.common.io.CharStreams;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class TestAcrCloudSignature {
private static final String ACCESS_KEY = "xxxx"; // confidential
private static final String SECRET_KEY = "yyyy"; // confidential
private static final String URL = "https://api.acrcloud.com/v1/audios";
private static HttpRequestFactory requestFactory = new NetHttpTransport().createRequestFactory();
private static final Logger logger = Loggers.getLogger();
public static void main(String [] args) {
String filePath = "/Users/serena/Desktop/ArcCloudMusic/Fernando.m4a";
String httpMethod = HttpMethod.POST.toString();
String httpUri = "/v1/audios";
String signatureVersion = "1";
long timestamp = System.currentTimeMillis();
String stringToSign = getStringToSign(httpMethod, httpUri, signatureVersion, timestamp);
String signature = getSignature(stringToSign);
logger.log(Level.INFO, "Timestamp:\t" + timestamp);
HttpResponse response = null;
try {
ImmutableMap<String, String> params = ImmutableMap.of(
"title", "fernando",
"audio_id", "1",
"bucket_name", "demo",
"data_type", "audio");
byte[] audio = getAudioFileTo(filePath);
String strResponse = sendMultiPartPostRequest(
"",
params,
ImmutableMap.of("audio-file", new Pair<>("Fernando.m4a", audio)),
signatureVersion,
signature,
timestamp);
logger.log(Level.INFO, "RESPONSE:" + strResponse);
} catch (Exception e) {
logger.log(Level.WARNING, "Response: " + response);
logger.log(Level.WARNING, "Exception: " + e.getMessage());
e.printStackTrace();
}
}
private static String getStringToSign(String method, String httpUri, String signatureVersion, long timestamp) {
String stringToSign = method+"\n"+httpUri+"\n"+ACCESS_KEY+"\n"+signatureVersion+"\n"+timestamp;
logger.log(Level.INFO, "String to Sign:\t" + stringToSign);
return stringToSign;
}
private static String getSignature(String stringToSign) {
String signature = BaseEncoding.base64().encode(hmacSha1(stringToSign));
logger.log(Level.INFO, "Signature:\t" + signature);
return signature;
}
private static byte[] hmacSha1(String toSign) {
try {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA1"));
return mac.doFinal(toSign.getBytes());
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
private enum HttpMethod {
GET, POST, PUT, DELETE,
}
private static byte[] getAudioFileTo(String filePath){
File file = new File(filePath);
byte[] buffer = null;
try {
InputStream fis = new FileInputStream(file);
buffer = new byte[(int) file.length()];
fis.read(buffer, 0, buffer.length);
fis.close();
} catch (IOException e) {
logger.log(Level.WARNING, "IOException: " + e.getMessage());
}
return buffer;
}
private static String sendMultiPartPostRequest(
String path,
ImmutableMap<String, String> parameters,
ImmutableMap<String, Pair<String, byte[]>> blobData,
String signatureVersion,
String signature,
long timestamp) {
try {
MultipartContent multipartContent = new MultipartContent();
multipartContent.setMediaType(new HttpMediaType("multipart/form-data"));
multipartContent.setBoundary("--------------------------0e94e468d6023641");
for (Entry<String, String> currentParameter : parameters.entrySet()) {
HttpHeaders headers = new HttpHeaders();
headers.clear();
headers.setAcceptEncoding(null);
headers.set("Content-Disposition", "form-data; name=\"" + currentParameter.getKey() + '\"');
HttpContent content = new ByteArrayContent(null, currentParameter.getValue().getBytes());
Part part = new Part(content);
part.setHeaders(headers);
multipartContent.addPart(part);
}
for (Entry<String, Pair<String, byte[]>> current : blobData.entrySet()) {
ByteArrayContent currentContent = new ByteArrayContent("application/octet-stream", current.getValue().second);
HttpHeaders headers = new HttpHeaders();
headers.clear();
headers.setAcceptEncoding(null);
headers.set("Content-Disposition", "form-data; name=\"" + current.getKey() + "\"; filename=\"" + current.getValue().first + '\"');
headers.setContentType("application/octet-stream");
multipartContent.addPart(new Part(headers, currentContent));
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
multipartContent.writeTo(out);
HttpResponse response = requestFactory
.buildPostRequest(new GenericUrl(URL + path), multipartContent)
.setHeaders(new HttpHeaders()
.set("access-key", ACCESS_KEY)
.set("signature-version", signatureVersion)
.set("signature", signature)
.set("timestamp", timestamp))
.execute();
String responseString = CharStreams.toString(new InputStreamReader(response.getContent()));
return responseString;
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static class Pair<A, B> {
final A first;
final B second;
Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
}
The error message I am getting from AcrCloud is:
500
{"name":"Internal Server Error","message":"There was an error at the server.","code":0,"status":500}
I am able to upload an audio file using this cUrl command:
Command: $ curl -H "access-key: xxxx" -H "signature-version: 1" -H
"timestamp: 1439958502089" -H "signature:
Nom6oajEzon260F2WzLpK3PE9e0=" -F "title=fernando" -F "audio_id=100" -F
"bucket_name=demo" -F "data_type=audio" -F
"audio_file=#/Users/serena/Desktop/ArcCloudMusic/Fernando.m4a"
https://api.acrcloud.com/v1/audios
Does anyone have any tips on how to debug this? Or has anyone had success using this service programmatically with Java? Or can someone show me how to print the contents of the HttpPOST request?
UPDATE I have also tried using their java example on GITHUB found here:
https://github.com/acrcloud/webapi_example/blob/master/RESTful%20service/UploadAudios.java
I get the same 500 error
UPDATE I no longer get the 500 error when I run their code. I fiddled with the apache jar versions and now I can successfully use the java code found on git hub. For record, The version that I used that work with their github code is apache-http-codec-1.10, apache-http-client-4.5, apache-http-core-4.4.1, apache-http-mime-4.5. When i used apache-http-core-4.5 it did not work.
UPDATE I have written a file that prints out the signatures generated by the java code on github reference above, and my own code. The signatures match so I am convinced that issue in the way I am constructing the multipart post request. I have also written the contents of both post requests to file and the headers contain different information in a few spots.
Thanks Serena for your patience, our team is doing a detailed analysis on the code and the apache jars now. Hopefully will have an update soon.
For now, if anyone who has the same problems, please use the following jars as mentioned in https://github.com/acrcloud/webapi_example/blob/master/RESTful%20service/UploadAudios.java
// import commons-codec-<version>.jar, download from http://commons.apache.org/proper/commons-codec/download_codec.cgi
import org.apache.commons.codec.binary.Base64;
// import HttpClient, download from http://hc.apache.org/downloads.cgi
/**
*
* commons-codec-1.1*.jar
* commons-logging-1.*.jar
* httpclient-4.*.jar
* httpcore-4.4.1.jar
* httpmime-4.*.jar
*
* */
Is there any way to map a image file using a spring controller? In my spring application, I want store the images in the directory src/main/resources (i'm using maven) and access them with a method like this:
#RequestMapping(value="image/{theString}")
public ModelAndView image(#PathVariable String theString) {
return new ModelAndView('what should be placed here?');
}
the string theString it's the image name (without extension). With this approach, I should be able to access my images this way:
/webapp/controller_mapping/image/image_name
Anyone can point a direction to do that?
You can return HttpEntity<byte[]>. Construct new instance providing image byte array and necessary headers like content length and mime type then return it from your method. Image bytes can be obtained using classloader getResourceAsStream method.
This works for me. It could use some cleaning up but it works. The ServiceException is just a simple base exception.
Good Luck!
package com.dhargis.example;
import java.io.File;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
#Controller
#RequestMapping("/image")
public class ImageController {
private static final Logger log = Logger.getLogger(ImageController.class);
private String filestore = "C:\\Users\\dhargis";
//produces = "application/octet-stream"
#RequestMapping(value = "/{filename:.+}", method = RequestMethod.GET)
public void get( #PathVariable String filename,
HttpServletRequest request,
HttpServletResponse response) {
log.info("Getting file " + filename);
try {
byte[] content = null;
File store = new File(filestore);
if( store.exists() ){
File file = new File(store.getPath()+File.separator+filename);
if( file.exists() ){
content = FileUtils.readFileToByteArray(file);
} else {
throw new ServiceException("File does not exist");
}
} else {
throw new ServiceException("Report store is required");
}
ServletOutputStream out = response.getOutputStream();
out.write(content);
out.flush();
out.close();
} catch (ServiceException e) {
log.error("Error on get", e);
} catch (IOException e) {
log.error("Error on get", e);
}
}
}
<!-- begin snippet: js hide: false -->