I am trying to download email attachment using gmail api, when I see the original email in browser, below is the content shown
Content-Type: application/pdf; name="Report.pdf"
Content-Disposition: attachment; filename="Report.pdf"
Content-Transfer-Encoding: base64
X-Attachment-Id: f_ikot3mys0
JVBERi0xLjQKJf////8KMTMgMCBvYmoKPDwvTGVuZ3RoIDI0NzYKL1N1YnR5cGUgL1hNTAovVHlw
ZSAvTWV0YWRhdGEKPj4Kc3RyZWFtCjw/eHBhY2tldCBiZWdpbj0n77u/JyBpZD0nVzVNME1wQ2Vo
aUh6cmVTek5UY3prYzlkJz8+Cjx4OnhtcG1ldGEgeDp4bXB0az0iMy4xLTcwMSIgeG1sbnM6eD0i
YWRvYmU6bnM6bWV0YS8iPgogIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcv
MTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91
dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICA8eG1w
OkNyZWF0ZURhdGU+MjAxNS0xMC0yN1QwNjozMTo1OFo8L3htcDpDcmVhdGVEYXRlPgogICAgICA8
eG1wOkNyZWF0b3JUb29sPk5pdHJvIFBybyA5ICAoOS4gMC4gNi4gMjApPC94bXA6Q3JlYXRvclRv
b2w+CiAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE1LTEwLTI3VDA2OjMxOjU5WjwveG1wOk1vZGlm
eURhdGU+CiAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTUtMTAtMjdUMDY6MzE6NTlaPC94bXA6
TWV0YWRhdGFEYXRlPgogICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgICA8cmRmOkRlc2NyaXB0aW9u
IHJkZjphYm91dD0iIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8i
PgogICAgICA8ZGM6Zm9ybWF0PmFwcGxpY2F0aW9uL3BkZjwvZGM6Zm9ybWF0PgogICAgICA8ZGM6
Y3JlYXRvcj4KICAgICAgICA8cmRmOlNlcT4KICAgICAgICAgIDxyZGY6bGk+PC9yZGY6bGk+CiAg
ICAgICAgPC9yZGY6U2VxPgogICAgICA8L2RjOmNyZWF0b3I+CiAgICAgIDxkYzp0aXRsZT4KICAg
ICAgICA8cmRmOkFsdD4KICAgICAgICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+PC9y
but when I use the gmail api below
curl https://www.googleapis.com/gmail/v1/users/sndata#celestica.com/messages/[mailid]/attachments/[attachmentid], it returned
{
"size": 4596,
"data": "JVBERi0xLjQKJf____8KMTMgMCBvYmoKPDwvTGVuZ3RoIDI0NzYKL1N1YnR5cGUgL1hNTAovVHlwZSAvTWV0YWRhdGEKPj4Kc3RyZWFtCjw_eHBhY2tldCBiZWdpbj0n77u_JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkJz8-Cjx4OnhtcG1ldGEgeDp4bXB0az0iMy4xLTcwMSIgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iPgogIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI-CiAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICA8eG1wOkNyZWF0ZURhdGU-MjAxNS0xMC0yN1QwNjozMTo1OFo8L3htcDpDcmVhdGVEYXRlPgogICAgICA8eG1wOkNyZWF0b3JUb29sPk5pdHJvIFBybyA5ICAoOS4gMC4gNi4gMjApPC94bXA6Q3JlYXRvclRvb2w-CiAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE1LTEwLTI3VDA2OjMxOjU5WjwveG1wOk1vZGlmeURhdGU-CiAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTUtMTAtMjdUMDY6MzE6NTlaPC94bXA6TWV0YWRhdGFEYXRlPgogICAgPC9yZGY6RGVzY3JpcHRpb24-CiAgICA8cmRmOkRlc2NyaXB0 .....
}
as you can see in the first line, some symbol "////" been converted to "____" which caused the file been decoded into a corrupted format.
Is this a bug in gmail api?
Thanks
Vincent
I just found out the gmail api return the attachment content in base64url format where the '+' and '/' characters of standard Base64 are respectively replaced by '-' and '_'.
import base64
#returned by gmail
gmail_data = {
"size": 4596,
"data": "JVBERi0xLjQKJf____8KMTMgMCBvYmoKPDwvTGVuZ3RoIDI0NzYKL1N1YnR5cGUgL1hNTAovVHlwZSAvTWV0YWRhdGEKPj4Kc3RyZWFtCjw_eHBhY2tldCBiZWdpbj0n77u_JyBpZD0nVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkJz8-Cjx4OnhtcG1ldGEgeDp4bXB0az0iMy4xLTcwMSIgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iPgogIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI-CiAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICA8eG1wOkNyZWF0ZURhdGU-MjAxNS0xMC0yN1QwNjozMTo1OFo8L3htcDpDcmVhdGVEYXRlPgogICAgICA8eG1wOkNyZWF0b3JUb29sPk5pdHJvIFBybyA5ICAoOS4gMC4gNi4gMjApPC94bXA6Q3JlYXRvclRvb2w-CiAgICAgIDx4bXA6TW9kaWZ5RGF0ZT4yMDE1LTEwLTI3VDA2OjMxOjU5WjwveG1wOk1vZGlmeURhdGU-CiAgICAgIDx4bXA6TWV0YWRhdGFEYXRlPjIwMTUtMTAtMjdUMDY6MzE6NTlaPC94bXA6TWV0YWRhdGFEYXRlPgogICAgPC9yZGY6RGVzY3JpcHRpb24-CiAgICA8cmRmOkRlc2NyaXB0'
}
file_data = base64.urlsafe_b64decode(gmail_data['data'])
Now you can save file_data into a file:
#you have to determine the file name
f = open('file.png', 'wb')
f.write(file_data)
f.close()
Related
I have an eml sample file which has an attachment but the content-disposition field is missing. The Microsoft Outlook treats the below mentioned body part as Attachment and displays the attachment file in the header field.
-----AU_MimePart_103082203221047146891954745
Content-Type: text/html;
charset=utf-8
Content-Transfer-Encoding: base64
//stream data
-----AU_MimePart_103082203221047146891954745--
How do I identify it as an attachment when the content-disposition filed is missing?
I have been trying to insert files using Salesforce Chatter REST API but its been documented for curl. I am unable to find how to do the same in Angularjs using $http with visualforce. I am not able to set the request header and body properly. Please provide the header and body contents for the request, if this has to be done using javascript rather than curl.
Example for creating a new Document
curl https://yourInstance.salesforce.com/services/data/v23.0/sobjects/Document/
-H "Authorization: Bearer token"
-H "Content-Type: multipart/form-data;boundary=\"boundary_string\""
--data-binary #newdocument.json
Example request body for creating a new Document
This code is the contents of newdocument.json. The binary data for the PDF content has been omitted for brevity and replaced
with “Binary data goes here.” An actual request contains the full binary content.
--boundary_string
Content-Disposition: form-data; name="entity_document";
Content-Type: application/json
{
"Description" : "Marketing brochure for Q1 2011",
"Keywords" : "marketing,sales,update",
"FolderId" : "005D0000001GiU7",
"Name" : "Marketing Brochure Q1",
"Type" : "pdf"
}
--boundary_string
Content-Type: application/pdf
Content-Disposition: form-data; name="Body"; filename="2011Q1MktgBrochure.pdf"
Binary data goes here.
--boundary_string--
I am trying to parse an email then save the actual email as a file in the file system for auditing and remove it from the exchange server. (same way like you do email save as in outlook)
So to do that I have found out that I can call
Message.getInputStream()
To retrieve the file bytes. Its working Ok, and I can write the email as a file to the file system.
my question is what is this file type? is it .eml or .msg? or something else?
when looking at its content I see text and not binary data
--_004_MWH_#TRUNCATED#_11namp_
Content-Type: multipart/alternative;
boundary="_000_MWHPR_#TRUNCATED#_1711namp_"
--_000_MWHPR10_#TRUNCATED#_HPR10MB1711namp_
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: base64
getInputStream doesn't say much about the type of data...
https://docs.oracle.com/javaee/7/api/javax/mail/Part.html#getInputStream--
Use getContentType on the message. Mime messages map to .eml on Windows.
You need to use Message.writeTo to save the message to a file.
I currently have a fully functional email notification system. It is all working correctly, but have noticed that one of the inline images, which is attached to the email via a rest service, is being requested three times.
It appears to be happening once transport.send is invoked. I'm assuming that when javamail goes to send email, it then calls for all inline images referenced and generates the base64 image and places it in the outgoing email.
My question is...
Is the above assumptions correct and why would it be calling the service multiple times when it has been verified in the raw email that it contains the image only once.
Below is a copy of the raw email with addresses changed, base64 images, text/plain and text/html versions removed.
Raw email
Date: Thu, 25 Feb 2016 20:40:04 -0800 (PST)
From: something.news.noreply#something.org
To: user.email#something.org
Message-ID: <56557433.31456461613977.JavaMail.something.news.noreply#something.orgh>
Subject: Business News for 02/26/2016
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_Part_6_670038123.1456461604268"
X-Priority: 3
------=_Part_6_670038123.1456461604268
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 7bit
TEXT VERSION OF EMAIL
------=_Part_6_670038123.1456461604268
Content-Type: multipart/related;
boundary="----=_Part_7_2073972040.1456461604268"
------=_Part_7_2073972040.1456461604268
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable
HTML VERSION OF EMAIL
------=_Part_7_2073972040.1456461604268
Content-Type: image/png;
name="840da574-a395-4fb8-8f33-bcbb1837220e?t=1452039853427"
Content-Transfer-Encoding: base64
Content-ID: <#9dc060f2-84ec-4344-8871-68d4e4885b70_840da574-a395-4fb8-8f33-bcbb1837220e?t=1452039853427>
Content-Disposition: inline;
filename="840da574-a395-4fb8-8f33-bcbb1837220e?t=1452039853427"
BASE 64 IMAGE
------=_Part_7_2073972040.1456461604268
Content-Type: image/png;
name="21ae1010-9675-4daa-8ac8-659052b943e8?t=1450724912940"
Content-Transfer-Encoding: base64
Content-ID: <#17ab1018-2bc5-44f5-9934-fb7b0f1a860b_21ae1010-9675-4daa-8ac8-659052b943e8?t=1450724912940>
Content-Disposition: inline;
filename="21ae1010-9675-4daa-8ac8-659052b943e8?t=1450724912940"
BASE 64 IMAGE
------=_Part_7_2073972040.1456461604268
Content-Type: image/png;
name="a96af9b6-9093-4086-bf91-e5677bef533f?t=1450966405365"
Content-Transfer-Encoding: base64
Content-ID: <#15a682c9-95e8-42ee-880b-f52a47396341_a96af9b6-9093-4086-bf91-e5677bef533f?t=1450966405365>
Content-Disposition: inline;
filename="a96af9b6-9093-4086-bf91-e5677bef533f?t=1450966405365"
BASE 64 IMAGE
------=_Part_7_2073972040.1456461604268
Content-Type: image/png;
name="25caf361-80a9-44c2-9ae6-97c19709becf?t=1450966427231"
Content-Transfer-Encoding: base64
Content-ID: <#2e9839ae-9c10-4c8f-9c4a-bc9e1a934fc3_25caf361-80a9-44c2-9ae6-97c19709becf?t=1450966427231>
Content-Disposition: inline;
filename="25caf361-80a9-44c2-9ae6-97c19709becf?t=1450966427231"
BASE 64 IMAGE
------=_Part_7_2073972040.1456461604268
Content-Type: image/png; name=upChart
Content-Transfer-Encoding: base64
Content-ID: <#6ca90a68-1d24-4414-ac8a-8c84b50bb663_upChart>
Content-Disposition: inline; filename=upChart
BASE 64 IMAGE
THIS IS THE IMAGE THAT IS RETRIEVED VIA A WEB SERVICE CALL. IT CREATES A CHART
IMAGE THAT GET ATTACHED TO EMAIL.
------=_Part_7_2073972040.1456461604268
Content-Type: image/png;
name="10739ce0-c979-4e47-8cee-666304b9a92e?t=1451426510519"
Content-Transfer-Encoding: base64
Content-ID: <#14227419-3db7-444b-a704-7abaccd1e380_10739ce0-c979-4e47-8cee-666304b9a92e?t=1451426510519>
Content-Disposition: inline;
filename="10739ce0-c979-4e47-8cee-666304b9a92e?t=1451426510519"
BASE 64 IMAGE
------=_Part_7_2073972040.1456461604268
Content-Type: image/png;
name="995344fa-765f-4a27-848c-d36d26c593f3?t=1451426553685"
Content-Transfer-Encoding: base64
Content-ID: <#f17f0535-a2a3-47a8-aca4-9ed883542de8_995344fa-765f-4a27-848c-d36d26c593f3?t=1451426553685>
Content-Disposition: inline;
filename="995344fa-765f-4a27-848c-d36d26c593f3?t=1451426553685"
BASE 64 IMAGE
------=_Part_7_2073972040.1456461604268--
------=_Part_6_670038123.1456461604268--
I have added I believe the relevant code. The code starts with what it started out like and then two changes I made based on the suggestion.
Started as:
try {
// Get a REST client so we can call the Highcharts service.
hcClient = new RestClient(hcServer, hcResource);
// Create a client response object.
ClientResponse cr = null;
// Initialize tries counter and loop a maximum of 3 tries
// if highcharts export server doesn't respond with a 200.
int tries = 0;
do {
// Increase tries count.
tries++;
// Call the service and place response data into our data object.
cr = hcClient.getWebResource().post(ClientResponse.class, params);
// Pull the response status from client.
upChartRespStatus = cr.getStatus();
// Determine if we received a 200 code or not.
if(upChartRespStatus == 200) {
// Obtain entity and build response.
response = Response
.ok(new ByteArrayInputStream(cr.getEntity(cbChart.getClass())))
.build();
} else {
// Log warning that chart was not received, what the response
// status was and what try we are on.
logger.warn("Unable to get requested chart, response code was "+upChartRespStatus+".");
logger.warn("Reattempting to get chart. The current count of retries is: "+tries);
}
} while (tries <= 3 && upChartRespStatus != 200);
} catch(Exception e) {
logger.error(e.getMessage());
} finally {
hcClient.destroy();
}
I thought maybe I would try sending a base64 encoded image. I didn't even get the image in the email doing this.
// Determine if we received a 200 code or not.
if(upChartRespStatus == 200) {
// Get entity from client request.
ByteArrayInputStream bais = new ByteArrayInputStream(cr.getEntity(cbChart.getClass()));
String b64Image = new String(Base64.encode(read(bais)));
// Obtain entity and build response.
response = Response
.ok(b64Image)
.header("Content-Type", "image/png")
.header("Content-Length", b64Image.length())
.header("Content-Transfer-Encoding", "BASE64")
.build();
} else {
// Log warning that chart was not received, what the response
// status was and what try we are on.
logger.warn("Unable to get requested chart, response code was "+upChartRespStatus+".");
logger.warn("Reattempting to get chart. The current count of retries is: "+tries);
}
My last attempt was going back to binary, but making sure that the headers were all in place. This last attempt did yield the chart image in the email, but still in all cases the transport.send is still calling the service three times before it actually sends.
// Determine if we received a 200 code or not.
if(upChartRespStatus == 200) {
// Get entity from client request.
ByteArrayInputStream bais = new ByteArrayInputStream(cr.getEntity(cbChart.getClass()));
// Obtain entity and build response.
response = Response
.ok(bais)
.header("Content-Type", "image/png")
.header("Content-Length", bais.available())
.header("Content-Transfer-Encoding", "BINARY")
.build();
} else {
// Log warning that chart was not received, what the response
// status was and what try we are on.
logger.warn("Unable to get requested chart, response code was "+upChartRespStatus+".");
logger.warn("Reattempting to get chart. The current count of retries is: "+tries);
}
Okay. So I got some success. Instead of setting headers at the service, I think that was to late. I took a look at the code that creates the email and specifically when the image gets added to its part. I've added what the code was and what I changed. By doing this, it is now only calling the service twice instead of three times.
private void addImagesInline(Multipart parent, List<URL> embeded, HashMap<String,String> cids) throws MessagingException {
if (embeded != null) {
for (URL img : embeded) {
final MimeBodyPart htmlPartImg = new MimeBodyPart();
DataSource htmlPartImgDs = new URLDataSource(img);
htmlPartImg.setDataHandler(new DataHandler(htmlPartImgDs));
String fileName = img.getFile();
fileName = getFileName(fileName);
String newFileName = cids.get(fileName);
boolean imageNotReferencedInHtml = newFileName == null;
if (imageNotReferencedInHtml) continue;
htmlPartImg.setHeader("Content-ID", "<"+newFileName+">");
htmlPartImg.setDisposition(BodyPart.INLINE);
htmlPartImg.setFileName(fileName);
parent.addBodyPart(htmlPartImg);
}
}
}
I added these to lines of code.
htmlPartImg.setHeader("Content-Type", "image/png");
htmlPartImg.setHeader("Content-Transfer-Encoding", "BASE64");
Thanks in advance for any replies!
I would expect the image to be requested twice. JavaMail will read the image once to determine what encoding is appropriate, based on the content of the image data. It will then read it again to encode it and include it in the message.
You can read the image yourself and save it locally to avoid multiple requests, or you can tell JavaMail what encoding to use by setting the Content-Transfer-Encoding header for the part; that way it won't have to guess what to use.
I'm trying to send a PDF attachment through JavaMail, but when I receive the email, I get something like this, without any attachments in the mail:
===EMAIL CONTENT===
------=_Part_0_3786439.1313701770148
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=ems_report.pdf
JVBERi0xLjQKJeLjz9MKMyAwIG9iago8PC9MZW5ndGggMTE4Ni9GaWx0ZXIvRmxhdGVEZWNvZGU+
PnN0cmVhbQp4nLWaTW/jNhCG7/oVBIoF0kMYfgw/1FviZNsEa+/WVrCHogc3VgIXsb1VnS7670vK
lhynMjmxRQSOBA1JPXpfaTSSyAgn55wworlw/x8W2V/ZVZFJTZTRpJhlN0X2aybInd/q2zHStC4W
2cVHTvzaY3b2Y/Gnb7trUg/G6pXqKROMqFy4QRU5l25LmT2+HtFtUoZRadpRrR/VLd2f6352M5yQ
8ctyWq1eljMyWC2+Pc+ny4eSjMtvq2pNHlcVGd0WNx6DkacOlN9+d8tZfSjtsNttb1q+4sqBKG6p
5S0XeK72sM52TLfL+Xo+Xa+qv8nkZbGYVv8iWfYFozlwd/CvlpbJ3O/LszBLOGOdEnJmqLUEbE7l
zhxW41Ln7fa3D/1xXi0OYwry3UXutv21C4x/bti3NIuMb9ees4nr33Tw5JvfXhcwTRe/5rscKwUY
s5FCdEghBdWcgJYdUgBpfl6Kd5wzWDgeoROOzhuFoDPG5D3TQYROSUsBUHQSemZTETYNnAqBYhM9
o4kImnyHbLpnNhlhA+HYJIrNLTX5kOCCCGUuwTSFnECuKJfhzPW5mpUuwY7Lh3L+TzlrQPcuLTeS
lVT5gd52L1br6fPhowunL96mPIFOebzNeeLUnAcRFRVzSQ8l4i79b+R8q6KSLtnrwyp+mk//eC6P
lRFaGRVaRmhlVKfKqCIyaiejwp2MMR214PV5fUjH0Wp5/nySlqrVUqO1VK2W+lQtRURL2dspKVNe
===CODE===
MimeMultipart multipart = new MimeMultipart();
MimeBodyPart part1 = new MimeBodyPart();
FileDataSource source = new FileDataSource("ems_report.pdf");
part1.setDataHandler(new DataHandler(source));
part1.setFileName(source.getName());
part1.setHeader("Content-Type", "application/pdf");
part1.setHeader("Content-Transfer-Encoding", "base64");
part1.setDisposition(Part.ATTACHMENT);
multipart.addBodyPart(part1);
MimeBodyPart part2 = new MimeBodyPart();
part2.setText("mimebodypart part2");
multipart.addBodyPart(part2);
message.setContent(multipart);
message.setHeader("Content-Type", "multipart/mixed");
What did I do wrong here? I've googled for the whole day and no one seems to have the solution. Any help is appreciated, thanks so much!
What you're seeing is a binary data of the multipart. You first have to decode the base64 string. After that you can process the PDF.
PS: If you open that mail in a client (Webmail, Outlook, Thunderbird) you should see the PDF correctly.