I want to test a method, which receives a CompletedFileUpload.
public void uploadZip(String orderId, CompletedFileUpload fileUpload) throws IOException {
Path path = Files.createTempFile("file-", "-temp");
File tempFile = path.toFile();
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
outputStream.write(fileUpload.getBytes());
someMethod(orderId, tempFile);
}
}
The CompletedFileUpload is an interface:
package io.micronaut.http.multipart;
public interface CompletedFileUpload extends FileUpload, CompletedPart {
}
I want to find a way to Mock the File upload, for spring boot i found :
MockMultipartFile multipartFile =
new MockMultipartFile("springboot.png", "springboot.png", "image/png", "".getBytes());
Is there something similar that I can use in Micronaut?
You can build your own MockFileUpload test class the way Spring does:
public class MockFileUpload implements CompletedFileUpload {
private final String filename;
private final MediaType mediaType;
private final byte[] content;
public MockFileUpload(String filename, MediaType mediaType) {
this(filename, mediaType, null);
}
public MockFileUpload(String filename, MediaType mediaType, #Nullable byte[] content) {
this.filename = filename;
this.mediaType = mediaType;
this.content = (content != null ? content : new byte[0]);
}
#Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(content);
}
#Override
public byte[] getBytes() throws IOException {
return content;
}
#Override
public ByteBuffer getByteBuffer() throws IOException {
return ByteBuffer.wrap(content);
}
#Override
public Optional<MediaType> getContentType() {
return Optional.of(mediaType);
}
#Override
public String getName() {
return filename;
}
#Override
public String getFilename() {
return filename;
}
#Override
public long getSize() {
return content.length;
}
#Override
public long getDefinedSize() {
return content.length;
}
#Override
public boolean isComplete() {
return true;
}
}
and then in your tests you can use the class this way:
CompletedFileUpload file = new MockFileUpload("invoice.pdf", MediaType.APPLICATION_PDF_TYPE);
Related
for several days I have been trying to implement the upload file in Java-GraphQL. I found this topic: How to upload files with graphql-java? I implemented second solutions.
public class FileUpload {
private String contentType;
private byte[] content;
public FileUpload(String contentType, byte[] content) {
this.contentType = contentType;
this.content = content;
}
public String getContentType() {
return contentType;
}
public byte[] getContent() {
return content;
}
}
public class MyScalars {
public static final GraphQLScalarType FileUpload = new GraphQLScalarType(
"FileUpload",
"A file part in a multipart request",
new Coercing<FileUpload, Void>() {
#Override
public Void serialize(Object dataFetcherResult) {
throw new CoercingSerializeException("Upload is an input-only type");
}
#Override
public FileUpload parseValue(Object input) {
if (input instanceof Part) {
Part part = (Part) input;
try {
String contentType = part.getContentType();
byte[] content = new byte[part.getInputStream().available()];
part.delete();
return new FileUpload(contentType, content);
} catch (IOException e) {
throw new CoercingParseValueException("Couldn't read content of the uploaded file");
}
} else if (null == input) {
return null;
} else {
throw new CoercingParseValueException(
"Expected type " + Part.class.getName() + " but was " + input.getClass().getName());
}
}
#Override
public FileUpload parseLiteral(Object input) {
throw new CoercingParseLiteralException(
"Must use variables to specify Upload values");
}
});
}
public class FileUploadResolver implements GraphQLMutationResolver {
public Boolean uploadFile(FileUpload fileUpload) {
String fileContentType = fileUpload.getContentType();
byte[] fileContent = fileUpload.getContent();
// Do something in order to persist the file :)
return true;
}
}
scalar FileUpload
type Mutation {
uploadFile(fileUpload: FileUpload): Boolean
}
I get this error during compilation:
Caused by: com.coxautodev.graphql.tools.SchemaClassScannerError: Expected a user-defined GraphQL scalar type with name 'FileUpload' but found none!
Have you registered it via RuntimeWiring?
Take a look here: Custom Scalar in Graphql-java
You have to extend GraphQLScalarType in your MyScalars class
I tested via Postman the application and get this warning:
"org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class sun.nio.ch.ChannelInputStream"
maybe someone knows how to resolve this problem?
code is presented below
My Controller
#RestController
public class FileServiceController {
private FileService fileService;
#Autowired
public FileServiceController(FileService fileService) {
this.fileService = fileService;
}
#CrossOrigin
#RequestMapping(value = "/api/v1/write")
public ResponseEntity writeToFile(#RequestParam final String sessionId, #RequestParam final String path) throws FileServiceException {
return path != null ? new ResponseEntity<>(fileService.openForWriting(sessionId, path),
HttpStatus.OK) : new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
#CrossOrigin
#RequestMapping(value = "/api/v1/files")
public ResponseEntity getFiles( #RequestParam final String sessionId, #RequestParam final String path) throws FileServiceException {
return path != null ? new ResponseEntity<>(fileService.getFiles(sessionId, path), HttpStatus.FOUND) :
new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
#CrossOrigin
#RequestMapping(value = "/api/v1/read")
public ResponseEntity readFromFile(#RequestParam final String sessionId, #RequestParam final String path) throws FileServiceException {
return path != null ? new ResponseEntity<>(fileService.openForReading(sessionId, path), HttpStatus.FOUND) :
new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
#CrossOrigin
#RequestMapping(value = "/api/v1/delete")
public ResponseEntity deleteFromFile(#RequestParam final String sessionId, #RequestParam final String path) throws FileServiceException {
return path != null ? new ResponseEntity<>(fileService.delete(sessionId, path), HttpStatus.OK) :
new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
}
My FileServiceImpl
#Service
public class FileServiceImpl implements FileService {
#Override
public OutputStream openForWriting(final String sessionId, final String path) throws FileServiceException {
try {
return Files.newOutputStream(Paths.get(path), StandardOpenOption.APPEND);
} catch (final IOException e) {
throw new FileServiceException("cannot open entry", e);
}
}
#Override
public InputStream openForReading(final String sessionId, final String path) throws FileServiceException {
try {
return Files.newInputStream(Paths.get(path));
} catch (final IOException e) {
throw new FileServiceException("cannot open entry", e);
}
}
#Override
public List<String> getFiles(final String sessionId, final String path) throws FileServiceException {
try (Stream<Path> paths = Files.walk(Paths.get(path))) {
return paths.filter(Files::isRegularFile)
.map(Path::toString)
.collect(Collectors.toList());
} catch (IOException e) {
throw new FileServiceException("cannot get files", e);
}
}
#Override
public boolean delete(final String sessionId, final String path) throws FileServiceException {
Path rootPath = Paths.get(path);
try {
Files.walk(rootPath)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
} catch (IOException e) {
throw new FileServiceException("cannot delete entries", e);
}
return true;
}
}
Interface
public interface FileService {
#NotNull OutputStream openForWriting(#NotNull final String sessionId, final String path) throws FileServiceException;
#NotNull InputStream openForReading(#NotNull final String sessionId, final String path) throws FileServiceException;
#NotNull List<String> getFiles(#NotNull final String sessionId, final String path) throws FileServiceException;
boolean delete(#NotNull final String sessionId, final String path) throws FileServiceException;
}
App
#SpringBootApplication
public class FileServiceApplication {
public static void main(final String[] args) {
SpringApplication.run(FileServiceApplication.class, args);
}
}
FileServiceImpl.openForReading() returns an InputStream and this is what you put in your response in FileServiceController.readFromFile(). The InputStream is not serializable, hence the exception.
Instead of an InputStream, you should put the content you read from it, e.g a byte[], a string or whatever object your application deals with.
This question already has answers here:
Configuring Spring MVC controller to send file to client
(2 answers)
Closed 8 years ago.
Given a simple Java Object:
public class Pojo {
private String x;
private String y;
private String z;
//... getters/setters ...
}
Is there some lib that i can put on my project that will make a controller like this:
#RequestMapping(value="/csv", method=RequestMethod.GET, produces= MediaType.TEXT_PLAIN)
#ResponseBody
public List<Pojo> csv() {
//Some code to get a list of Pojo objects
//...
return myListOfPojos;
}
To produce a csv file of my Pojos? For a Json result, i use Jackson lib. I need another lib for CSV results.
As a simple variant. You can generate csv by any way you want and return it as String.
Something like this:
#RequestMapping(value="/csv", method=RequestMethod.GET)
#ResponseBody
public String csv() {
//Some code to get a list of Pojo objects
//...
StringBuilder sb = new StringBuilder();
for (Pojo pojo: myListOfPojos){
sb.append(pojo.getX());
sb.append(",");
sb.append(pojo.getY());
sb.append(",");
sb.append(pojo.getZ());
sb.append("\n");
}
return sb.toString;
}
Should work.
Autogenerate this strings by reflection looks like simple work too.
Based on another question, i did my own HTTPMessageConverter for Tsv Responses.
TsvMessageConverter.java
public class TsvMessageConverter extends AbstractHttpMessageConverter<TsvResponse> {
public static final MediaType MEDIA_TYPE = new MediaType("text", "tsv", Charset.forName("utf-8"));
private static final Logger logger = LoggerFactory.getLogger(TsvMessageConverter.class);
public TsvMessageConverter() {
super(MEDIA_TYPE);
}
protected boolean supports(Class<?> clazz) {
return TsvResponse.class.equals(clazz);
}
#Override
protected TsvResponse readInternal(Class<? extends TsvResponse> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
protected void writeInternal(TsvResponse tsvResponse, HttpOutputMessage output) throws IOException, HttpMessageNotWritableException {
output.getHeaders().setContentType(MEDIA_TYPE);
output.getHeaders().set("Content-Disposition", "attachment; filename=\"" + tsvResponse.getFilename() + "\"");
final OutputStream out = output.getBody();
writeColumnTitles(tsvResponse, out);
if (tsvResponse.getRecords() != null && tsvResponse.getRecords().size() != 0) {
writeRecords(tsvResponse, out);
}
out.close();
}
private void writeRecords(TsvResponse response, OutputStream out) throws IOException {
List<String> getters = getObjectGetters(response);
for (final Object record : response.getRecords()) {
for (String getter : getters) {
try {
Method method = ReflectionUtils.findMethod(record.getClass(), getter);
out.write(method.invoke(record).toString().getBytes(Charset.forName("utf-8")));
out.write('\t');
} catch (IllegalAccessException | InvocationTargetException e) {
logger.error("Erro ao transformar em CSV", e);
}
}
out.write('\n');
}
}
private List<String> getObjectGetters(TsvResponse response) {
List<String> getters = new ArrayList<>();
for (Method method : ReflectionUtils.getAllDeclaredMethods(response.getRecords().get(0).getClass())) {
String methodName = method.getName();
if (methodName.startsWith("get") && !methodName.equals("getClass")) {
getters.add(methodName);
}
}
sort(getters);
return getters;
}
private void writeColumnTitles(TsvResponse response, OutputStream out) throws IOException {
for (String columnTitle : response.getColumnTitles()) {
out.write(columnTitle.getBytes());
out.write('\t');
}
out.write('\n');
}
}
TsvResponse.java
public class TsvResponse {
private final String filename;
private final List records;
private final String[] columnTitles;
public TsvResponse(List records, String filename, String ... columnTitles) {
this.records = records;
this.filename = filename;
this.columnTitles = columnTitles;
}
public String getFilename() {
return filename;
}
public List getRecords() {
return records;
}
public String[] getColumnTitles() {
return columnTitles;
}
}
And on SpringContext.xml add the following:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="com.mypackage.TsvMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
So, you can use on your controller like this:
#RequestMapping(value="/tsv", method= RequestMethod.GET, produces = "text/tsv")
#ResponseBody
public TsvResponse tsv() {
return new TsvResponse(myListOfPojos, "fileName.tsv",
"Name", "Email", "Phone", "Mobile");
}
Spring portlet JSP, Making ajax request and in controller trying to get jsp page so that i can pass and generate pdf output.
But problem is didn't get any string data but html contents are returned on jsp page please check code as follwoing
#Controller("exportSummaryController")
#RequestMapping(value = "VIEW")
public class ExportSummaryController implements PortletConfigAware {
#ResourceMapping("exportAccRequest")
public void accountRollupAction(#RequestParam("accNum") String accNum,
#RequestParam("sourceId") String sourceId, #RequestParam("serviceId") String serviceId,
#RequestParam("summaryExport") String strExport, ResourceRequest request, ResourceResponse response) throws Exception {
//processing data
ResourceResponseWrapper responseWrapper = new ResourceResponseWrapper(response) {
private final StringWriter sw = new StringWriter();
#Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(sw);
}
#Override
public OutputStream getPortletOutputStream() throws IOException {
return(new StringOutputStream(sw));
}
#Override
public String toString() {
return sw.toString();
}
};
portletConfig.getPortletContext().getRequestDispatcher("/WEB-INF/jsp/account_summary.jsp").include(request, responseWrapper);
String content = responseWrapper.toString();
System.out.println("Output : " + content); // here i found empty output on command line but output is returned to jsp page.
}
}
public class StringOutputStream extends OutputStream {
private StringWriter stringWriter;
public StringOutputStream(StringWriter stringWriter) {
this.stringWriter = stringWriter;
}
public void write(int c) {
stringWriter.write(c);
}
}
In your code the output is cosumed by only one OutputStream.
Try this,
ResourceResponseWrapper responseWrapper = new ResourceResponseWrapper(response) {
private final StringWriter sw = new StringWriter();
#Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(sw){
#Override
public void write(String s, int off, int len)
{
try
{ sw.write(s, off, len);
response.getWriter().write(s, off, len);
}
catch (IOException e)
{
e.printStackTrace();
}
}
};
}
#Override
public String toString() {
return sw.toString();
}
};
I think my scenario is pretty common. I have a database and I want my Spring MVC app to accept a request in the controller, invoke the DB service to get data and send that data to the client as a CSV file. I'm using the JavaCSV library found here to assist in the process: http://sourceforge.net/projects/javacsv/
I've found several examples of people doing similar things and cobbled together something that looks correct-ish. When I hit the method, though, nothing is really happening.
I thought writing the data to the HttpServletResponse's outputStream would be sufficient, but apparently, I'm missing something.
Here's my controller code:
#RequestMapping(value="/getFullData.html", method = RequestMethod.GET)
public void getFullData(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException{
List<CompositeRequirement> allRecords = compReqServ.getFullDataSet((String)session.getAttribute("currentProject"));
response.setContentType("data:text/csv;charset=utf-8");
response.setHeader("Content-Disposition","attachment; filename=\yourData.csv\"");
OutputStream resOs= response.getOutputStream();
OutputStream buffOs= new BufferedOutputStream(resOs);
OutputStreamWriter outputwriter = new OutputStreamWriter(buffOs);
CsvWriter writer = new CsvWriter(outputwriter, '\u0009');
for(int i=1;i <allRecords.size();i++){
CompositeRequirement aReq=allRecords.get(i);
writer.write(aReq.toString());
}
outputwriter.flush();
outputwriter.close();
};
What step am I missing here? Basically, the net effect is... nothing. I would have thought setting the header and content type would cause my browser to pick up on the response and trigger a file download action.
It seems to be because your Content-type is set incorrectly, it should be response.setContentType("text/csv;charset=utf-8") instead of response.setContentType("data:text/csv;charset=utf-8").
Additionally, if you are using Spring 3, you should probably use a #ResponseBody HttpMessageConverter for code reuse. For example:
In the controller:
#RequestMapping(value = "/getFullData2.html", method = RequestMethod.GET, consumes = "text/csv")
#ResponseBody // indicate to use a compatible HttpMessageConverter
public CsvResponse getFullData(HttpSession session) throws IOException {
List<CompositeRequirement> allRecords = compReqServ.getFullDataSet((String) session.getAttribute("currentProject"));
return new CsvResponse(allRecords, "yourData.csv");
}
plus a simple HttpMessageConverter:
public class CsvMessageConverter extends AbstractHttpMessageConverter<CsvResponse> {
public static final MediaType MEDIA_TYPE = new MediaType("text", "csv", Charset.forName("utf-8"));
public CsvMessageConverter() {
super(MEDIA_TYPE);
}
protected boolean supports(Class<?> clazz) {
return CsvResponse.class.equals(clazz);
}
protected void writeInternal(CsvResponse response, HttpOutputMessage output) throws IOException, HttpMessageNotWritableException {
output.getHeaders().setContentType(MEDIA_TYPE);
output.getHeaders().set("Content-Disposition", "attachment; filename=\"" + response.getFilename() + "\"");
OutputStream out = output.getBody();
CsvWriter writer = new CsvWriter(new OutputStreamWriter(out), '\u0009');
List<CompositeRequirement> allRecords = response.getRecords();
for (int i = 1; i < allRecords.size(); i++) {
CompositeRequirement aReq = allRecords.get(i);
writer.write(aReq.toString());
}
writer.close();
}
}
and a simple object to bind everything together:
public class CsvResponse {
private final String filename;
private final List<CompositeRequirement> records;
public CsvResponse(List<CompositeRequirement> records, String filename) {
this.records = records;
this.filename = filename;
}
public String getFilename() {
return filename;
}
public List<CompositeRequirement> getRecords() {
return records;
}
}
Based on Pierre answer, i did a converter. Here is the full code, that works with any Object passed:
TsvMessageConverter.java
public class TsvMessageConverter extends AbstractHttpMessageConverter<TsvResponse> {
public static final MediaType MEDIA_TYPE = new MediaType("text", "tsv", Charset.forName("utf-8"));
private static final Logger logger = LoggerFactory.getLogger(TsvMessageConverter.class);
public TsvMessageConverter() {
super(MEDIA_TYPE);
}
protected boolean supports(Class<?> clazz) {
return TsvResponse.class.equals(clazz);
}
#Override
protected TsvResponse readInternal(Class<? extends TsvResponse> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
return null;
}
protected void writeInternal(TsvResponse tsvResponse, HttpOutputMessage output) throws IOException, HttpMessageNotWritableException {
output.getHeaders().setContentType(MEDIA_TYPE);
output.getHeaders().set("Content-Disposition", "attachment; filename=\"" + tsvResponse.getFilename() + "\"");
final OutputStream out = output.getBody();
writeColumnTitles(tsvResponse, out);
if (tsvResponse.getRecords() != null && tsvResponse.getRecords().size() != 0) {
writeRecords(tsvResponse, out);
}
out.close();
}
private void writeRecords(TsvResponse response, OutputStream out) throws IOException {
List<String> getters = getObjectGetters(response);
for (final Object record : response.getRecords()) {
for (String getter : getters) {
try {
Method method = ReflectionUtils.findMethod(record.getClass(), getter);
out.write(method.invoke(record).toString().getBytes(Charset.forName("utf-8")));
out.write('\t');
} catch (IllegalAccessException | InvocationTargetException e) {
logger.error("Erro ao transformar em CSV", e);
}
}
out.write('\n');
}
}
private List<String> getObjectGetters(TsvResponse response) {
List<String> getters = new ArrayList<>();
for (Method method : ReflectionUtils.getAllDeclaredMethods(response.getRecords().get(0).getClass())) {
String methodName = method.getName();
if (methodName.startsWith("get") && !methodName.equals("getClass")) {
getters.add(methodName);
}
}
sort(getters);
return getters;
}
private void writeColumnTitles(TsvResponse response, OutputStream out) throws IOException {
for (String columnTitle : response.getColumnTitles()) {
out.write(columnTitle.getBytes());
out.write('\t');
}
out.write('\n');
}
}
TsvResponse.java
public class TsvResponse {
private final String filename;
private final List records;
private final String[] columnTitles;
public TsvResponse(List records, String filename, String ... columnTitles) {
this.records = records;
this.filename = filename;
this.columnTitles = columnTitles;
}
public String getFilename() {
return filename;
}
public List getRecords() {
return records;
}
public String[] getColumnTitles() {
return columnTitles;
}
}
And on SpringContext.xml add the following:
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="com.mypackage.TsvMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
So, you can use on your controller like this:
#RequestMapping(value="/tsv", method= RequestMethod.GET, produces = "text/tsv")
#ResponseBody
public TsvResponse tsv() {
return new TsvResponse(myListOfPojos, "fileName.tsv",
"Name", "Email", "Phone", "Mobile");
}