How to send attachment through Mailjet in JAVA - java

I want to send mail with an attachment through Mailjet in java. I have no problem while sending simple mail without attachment but when I try to add attachment I am getting this error:
400
[{"ErrorIdentifier":"314408e7-e528-469f-9361-2eb3c24b2b32","ErrorCode":"mj-0004","ErrorRelatedTo":["Messages.Attachments"],"ErrorMessage":"Type mismatch. Expected type \"Attachments\".","StatusCode":400}]
And my code looks like this:
#Service
public class MailJetSenderImp implements MailJetSender {
Message message = new Message();
#Override
public ResponseEntity<Message> sendMail(String To, String Body, String Subject, File attachment) throws MailjetSocketTimeoutException, JSONException, IOException {
FileInputStream fileInputStream = new FileInputStream(attachment);
int byteLength=(int) attachment.length(); //bytecount of the file-content
byte[] filecontent = new byte[byteLength];
fileInputStream.read(filecontent,0,byteLength);
byte[] encoded = Base64.getEncoder().encode(filecontent);
MailjetRequest email = new MailjetRequest(Emailv31.resource)
.property(Emailv31.MESSAGES, new JSONArray()
.put(new JSONObject()
.put(Emailv31.Message.FROM, new JSONObject()
.put("Email","xxxxxx#gmail.com" )
.put("Name", "xxxxx"))
.put(Emailv31.Message.TO, new JSONArray()
.put(new JSONObject()
.put("Email", To)))
.put(Emailv31.Message.SUBJECT, Subject)
.put(Emailv31.Message.TEXTPART, "")
.put(Emailv31.Message.HTMLPART, Body)
.put(Emailv31.Message.ATTACHMENTS,encoded)));
final String mailjetApiKey = "xxxxxxxx";
final String mailjetSecretKey = "yyyyyyyy";
MailjetClient client = new MailjetClient(
mailjetApiKey, mailjetSecretKey, new ClientOptions("v3.1"));
try {
// trigger the API call
MailjetResponse response = client.post(email);
// Read the response data and status
System.out.println(response.getStatus());
System.out.println(response.getData());
message.setCode(response.getStatus());
message.setMessage(response.getData().toString());
return ResponseEntity.status(HttpStatus.OK).body(message);
} catch (MailjetException e) {
System.out.println("Mailjet Exception: " + e);
message.setCode(400);
message.setMessage("could not send email");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(message);
}
}
}
I get error message on (.put(Emailv31.Message.ATTACHMENTS,encoded)));

Here is the Java code to send the mail attachment
The ATTACHMENTS data is a JSON array containing 3 fields:
ContentType - Content type of the attachment
Filename - name of the file attachment that the receiver would see
Base64Content - Base64 encoded file data as String
So, encode the file content as String ( I used the Base64 encoder from mailjet client JAR itself here ). filecontent is the byte[]. A hardcoded PDF file code sample below :
java.nio.file.Path pdfPath =
java.nio.file.Paths.get("c:\\D\\sample.pdf");
byte[] filecontent = java.nio.file.Files.readAllBytes(pdfPath);
String fileData = com.mailjet.client.Base64.encode(filecontent);
Next, use this code to send the attachment, your other code remains same.
Example here is for a PDF file, choose your MIME type correctly
.put(Emailv31.Message.ATTACHMENTS,
new JSONArray().put(new JSONObject().put("ContentType", "application/pdf")
.put("Filename", "abc.pdf")
.put("Base64Content", fileData)))
.put(Emailv31.Message.HTMLPART,...

Related

How to return both JSON response and Byte Array of file in Springboot RestController Response while downloading a file

I am developing a rest controller to download a .docx file into the client system. My code is working fine as the file is getting downloaded. Now I want to enhance the response. My requirement is to also send a JSON payload in the response along with the .docx file content, something like
{"message":"Report downloaded Successfully"}
incase of successful download or with a different message incase of failure.
Below is my restcontroller code:
#RestController
public class DownloadController {
#PostMapping(value="/download",
consumes = {"multipart/form-data"},
produces = {"application/octet-stream"})
public ResponseEntity<?> downloadFile(#RequestParam("file") MultipartFile uploadFile){
//business logic to create the attachment file
try {
File file = new File("path_to_.DOCX file_I_have_created");
byte[] contents = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setContentDisposition(ContentDisposition.attachment().filename("survey.docx").build());
return new ResponseEntity<>(contents, headers, HttpStatus.OK);
} catch (Exception e){
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
How do I modify my response code to send both the JSON message and the byte[] contents so that the file gets downloaded and I can see the JSON message in the response preview tab in the chrome or response body in postman?
UPDATE: I tried to define a response class like below
public class Downloadresponse {
private byte[] content;
private String message;
//getter,setters
}
With this change in place, I am getting below exception:
Resolved [ HttpMessageNotWritableException: No converter for [class ...Downloadresponse] with preset content-type "application/octet-stream”]
You can't. HTTP doesn't allow you to defined multiple content-types on 1 request/response. That being said, you could send the byte array base64 encoded as part of a json response but would need to handle it in the front-end (if you have any) as it would not trigger the file download process of the browser.
You can define custom class which hold your current content and message . So you can return that class in the response
ResponseClass
{
byte[] contents;
String message;
}
You can send a json object that contain the message and the file as encoded64 string.
And in the client side you decode it and download it.
public record MyRecord(String message, String encodedStringBase64, String filename) {}
...
try {
File file = new File("path_to_.DOCX file_I_have_created");
byte[] contents = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
String encodedString = Base64.getEncoder().encodeToString(contents);
MyRecord record = new MyRecord("Report downloaded Successfully", encodedString, file.getName());
return log.traceExit(ResponseEntity.ok().headers(headers)
.contentType(MediaType.valueOf("application/json")).body(record));
} ...

Post to API with two field and one xml file receiving answer back file not good

Hello I am building an application in Spring Boot and Thymeleaf and I have an upload page where I have two fields and an upload button. I complete the fields and I select and xml and send it to the api and the answer I receive back is that the file is not valid. The response is written by the api creator so probably what I am thinking I am not reading the file right before sending.
Here is my code:
Service:
public String upload(String standard, String cif, MultipartFile file) throws
IOException {
String url = "http://100.000:222/test/upload?standard="+standard+"&cif="+cif;
String content = file.toString();
InputStream inputStream = new ByteArrayInputStream(content.getBytes());
String text = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))
.lines()
.collect(Collectors.joining("\n"));
HttpHeaders headers = new HttpHeaders();
headers.add("serial", "123");
headers.add("Content-Disposition", "text/xml");
headers.add("fisier", text);
RestTemplate restTemplate = new RestTemplate();
RequestEntity<Void> request = (RequestEntity<Void>) RequestEntity.get(URI.create(url))
.headers(headers)
.build();
String response = restTemplate.postForObject(url, request, String.class);
return response;
}
Here is the controller:
#PostMapping("/upload")
public String postUpload(#RequestParam("standard") String standard, #RequestParam("select") String select, #RequestParam("fisier") MultipartFile file, Model model) throws IOException {
String index = service.upload(standard, select, file);
System.out.println(index);
model.addAttribute("index", index);
return "upload";
}
This is the message I am getting back:
<header xmlns = "xxx: xxx: xxx: xx: respUploadFile: v1" dateResponse = "202205311034" ExecutionStatus = "1">
<Errors errorMessage = "The transmitted file is invalid. Org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog." />
</header>
Can someone tell me what I am doing wrong or give me an working example. Thanks

Cannot create the generated HTTP client's required return type, since no TypeConverter from ByteBuffer to class java.io.File : Micronaut

Below is the server side code for sending file to client as rest response using micronaut.
#Get(value = "/downloadFile", produces = MediaType.APPLICATION_OCTET_STREAM )
public HttpResponse<File> downloadDocument() throws IOException {
File sampleDocumentFile = new File(getClass().getClassLoader().getResource("SampleDocument.pdf").getFile());
return HttpResponse.ok(sampleDocumentFile).header("Content-Disposition", "attachment; filename=\"" + sampleDocumentFile.getName() + "\"" );
}
Below is the client for calling the above endpoint.
#Client(value = "/client")
public interface DownloadDocumentClient {
#Get(value = "/downloadDocument", processes = MediaType.APPLICATION_OCTET_STREAM)
public Flowable<File> downloadDocument();
}
Tried to retreive file as below :-
Flowable<File> fileFlowable = downloadDocumentClient.downloadDocument();
Maybe<File> fileMaybe = fileFlowable.firstElement();
return fileMaybe.blockingGet();
Getting exception as
io.micronaut.context.exceptions.ConfigurationException: Cannot create
the generated HTTP client's required return type, since no
TypeConverter from ByteBuffer to class java.io.File is registered
You cannot send file data using File instance because it contains only path and not file content. You can send file content using byte array.
Update the controller in this way:
#Get(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM)
public HttpResponse<byte[]> downloadDocument() throws IOException, URISyntaxException {
String documentName = "SampleDocument.pdf";
byte[] content = Files.readAllBytes(Paths.get(getClass().getClassLoader().getResource(documentName).toURI()));
return HttpResponse.ok(content).header("Content-Disposition", "attachment; filename=\"" + documentName + "\"");
}
Client will be then like this:
#Get(value = "/download", processes = MediaType.APPLICATION_OCTET_STREAM)
Flowable<byte[]> downloadDocument();
And finally client call:
Flowable<byte[]> fileFlowable = downloadDocumentClient.downloadDocument();
Maybe<byte[]> fileMaybe = fileFlowable.firstElement();
byte[] content = fileMaybe.blockingGet();
Update:
If you need to save received bytes (file content) into the file on a client machine (container) then you can do that for example like this:
Path targetPath = Files.write(Paths.get("target.pdf"), fileMaybe.blockingGet());
And if you really need instance of File instead of Path for further processing then simply:
File file = targetPath.toFile();

How can i upload file from console with other data

I would like to upload file to my web server.
My server implement RESTful API.
To upload file i should pass 4 parameters: file, file_name, project, version
I would like to upload file with this 3 parameters from console. I tried this
URL obj = new URL(MY_URL);
HttpsURLConnection con = (HttpsURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setDoOutput(true);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(con.getOutputStream());
ToSend send = new ToSend();
send.file = Files.readAllBytes(Paths.get("path"));
send.file_name = "name";
send.project = "test";
send.version = "1";
objectOutputStream.writeObject(send);
objectOutputStream.flush();
objectOutputStream.close();
and the object i send look like this
class ToSend implements Serializable {
byte[] file;
String file_name;
String project;
String version;
}
I get Exception in thread "main" java.io.IOException: Server returned HTTP response code: 400 for URL:
If this is important my server written in Java+Spring and method for this has next signature
#RequestMapping(value = "/", method = RequestMethod.POST)
public #ResponseBody
Response<Boolean> upload(
#RequestParam("project") String project,
#RequestParam("version") String version,
#RequestParam("file_name") String fileName,
#RequestParam("file") MultipartFile file
) throws ServiceException, BadFileExtension {
boolean success = storageService.uploadFile(file, project, fileName, version);
return builder.get(success);
}
This solutions works for me. Maybe useful for someone too.
http://www.codejava.net/java-se/networking/upload-files-by-sending-multipart-request-programmatically

Send email with javax.mail using an existing InputStream as attachment content

Is it possible to send an email using javax.mail and using an “existing” InputStream for the email message attachment content?
Currently I am building the email message as follows:
final MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress(from));
message.addRecipient(Message.RecipientType.TO, new InternetAddress(to));
message.setSubject("Subject line");
final Multipart multipartContent = new MimeMultipart();
final MimeBodyPart textPart = new MimeBodyPart();
textPart.setText("Message body");
multipartContent.addBodyPart(textPart);
final MimeBodyPart attachmentPart = new MimeBodyPart();
final DataSource source = new InputStreamDataSource("text/plain", "test.txt", new ByteArrayInputStream("CONTENT INPUT STREAM".getBytes()));
attachmentPart.setDataHandler(new DataHandler(source));
attachmentPart.setFileName("text.txt");
multipartContent.addBodyPart(attachmentPart);
message.setContent(multipartContent);
InputStreamDataSource is implemented as follows:
public class InputStreamDataSource implements DataSource
{
private final String contentType;
private final String name;
private final InputStream inputStream;
public InputStreamDataSource(String contentType, String name, InputStream inputStream)
{
this.contentType = contentType;
this.name = name;
this.inputStream = inputStream;
}
public String getContentType()
{
return contentType;
}
public String getName()
{
return name;
}
public InputStream getInputStream() throws IOException
{
System.out.println("CALLED TWICE: InputStreamDataSource.getInputStream()");
return new BufferedInputStream(inputStream);
//return new ByteArrayInputStream("THIS 'NEW' INPUT STREAM WORKS BUT 'EXISTING' INPUT STREAM RESULTS IN ZERO-BYTE ATTACHMENT".getBytes());
}
public OutputStream getOutputStream() throws IOException
{
throw new UnsupportedOperationException("Not implemented");
}
}
The DataSource provides method getInputStream() to get the InputStream for the email message attachment content.
If I return a "new" InputStream which does not depend on an "existing" InputStream then it works fine. But if I return an “existing” InputStream then the email message is delivered with a zero-byte attachment.
Is it possible to send an email using javax.mail, and use an “existing” InputStream for the email message attachment content?
EDIT:
see https://community.oracle.com/thread/1590625
TL;DR use a ByteArrayDataSource
One has to delve into Oracle's source code... https://java.net/projects/javamail/sources/mercurial/content/mail/src/main/java/javax/mail/internet/MimeBodyPart.java
The current java mail implementation goes 2 times over the input stream:
First to determine whether it should set the header "Content-Transfer-Encoding" to 7 or 8 bits (see Content Transfer Encoding 7bit or 8 bit)
Then a second time when it actually writes the message
...which kind of sucks because the whole stream (maybe hundreds of MB over a slow connection) will be read two times ...and leads to exactly this issue for streams that are "consumed" once read.
The first "workaround" I tried is to specify the headers yourself:
attachmentPart.setDataHandler(new DataHandler(source));
attachmentPart.setHeader("Content-Transfer-Encoding", "8bit");
attachmentPart.setHeader("Content-Type", ds.getContentType() + "; " + ds.getName());
...and in that order, and not the other way round ...because for some reason setDataHandler calls internally another method invalidateContentHeaders which clears the "Content-Transfer-Encoding" header again (wtf?!)
Sounded great, the mail was sent, hooray!!! :D ... :( see next
Attachment send ...but broken
The received file in my mail server is broken. Huh. Why?!. After a long search and delving again in this crappy java mail code, I found it, they pipe the InputStream into a LineOutputStream which changes the line endings of your binary data. Meh. The java mail implementation is really a mess. :/
I rewrited your InputStreamDataSource class, and it works for me.
class InputStreamDataSource implements DataSource {
String contentType;
String name;
byte[] fileData;
public InputStreamDataSource(String contentType, String name, InputStream inputStream) throws IOException {
this.contentType = contentType;
this.name = name;
/**
* It seems DataSource will close inputStream and reopen it.
* I converted inputStream to a byte array, so it won't be closed again.
*/
fileData = IOUtils.toByteArray(inputStream);
}
public String getContentType() {
return contentType;
}
public String getName() {
return name;
}
public InputStream getInputStream() throws IOException {
/**
* Convert byte array back to inputStream.
*/
return new ByteArrayInputStream(fileData);
}
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Not implemented");
}
}
I solved it converting the InputStream to a byte array and converting it to Base64 format.
//get file name
String fileName = ...;
//get content type
String fileContentType = ...;
//get file content
InputStream fileStream = ...;
//convert to byte array
byte[] fileByteArray = IOUtils.toByteArray(fileStream);
//and convert to Base64
byte[] fileBase64ByteArray = java.util.Base64.getEncoder().encode(fileByteArray);
//manually define headers
InternetHeaders fileHeaders = new InternetHeaders();
fileHeaders.setHeader("Content-Type", fileContentType + "; name=\"" + fileName + "\"");
fileHeaders.setHeader("Content-Transfer-Encoding", "base64");
fileHeaders.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
//build MIME body part
MimeBodyPart mbp = new MimeBodyPart(fileHeaders, fileBase64ByteArray);
mbp.setFileName(fileName);
//add it to the multipart
multipart.addBodyPart(mbp);
If the InputStream contains mime headers then use the javax.mail.internet.MimeBodyPart(InputStream) constructor. You don't need to use a custom DataSource class.
Otherwise, if the InputStream is just the body without headers then convert the stream into a byte array and use the javax.mail.internet.MimeBodyPart(InternetHeaders, byte[]) constructor to provide your headers.
I use this code for sending email with web downloaded attachment. You can easily edit it for your purpose. In mimeType use mime type of your attachment. Happy coding.
try {
Message message = new MimeMessage(session);
message.setFrom(new InternetAddress(
"sender#gmail.com"));
message.setRecipients(Message.RecipientType.TO,
InternetAddress.parse("reciever#gmail.com"));
message.setSubject("subject");
Multipart multipart = new MimeMultipart();
URL url = new URL(url);
InputStream is = url.openStream();
MimeBodyPart bodyPart = new MimeBodyPart(is);
multipart.addBodyPart(bodyPart);
message.setContent(multipart);
message.addHeader("Content-Type", mimeType);
Transport.send(message);
logger.info("SENT to" + message.getRecipients(RecipientType.TO));
} catch (MessagingException e) {
//some implementation
}
The current java mail implementation goes over the input stream twice: the first pass to detect the encoding for the data and the second one to send the data.
You can prevent the first pass if you specify the encoding using the EncodingAware interface. The supplied DataSource should implement this interface. Here is an example:
public class AttachementDataSource implements javax.activation.DataSource, javax.mail.EncodingAware {
private final InputStreamSource inputStreamSource;
public AttachementDataSource(InputStreamSource inputStreamSource) {
this.inputStreamSource = inputStreamSource;
}
#Override
public InputStream getInputStream() throws IOException {
return inputStreamSource.getInputStream();
}
#Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException("Read-only javax.activation.DataSource");
}
#Override
public String getContentType() {
return "application/octet-stream";
}
#Override
public String getName() {
return "inline";
}
#Override
public String getEncoding() {
return "base64";
}
}

Categories