vCard is popular format for exchanging contacts. Besides e-mail clients also modern mobile phones are able to read and send contacts as vCard. This posting introduces same ideas how to implement vCard support in your applications and how to add images to vCards.
ASP.NET Core version of this solution is available at my blog post Creating vCard in ASP.NET Core.
Our goal is make our system to output vCard like shown on image. We will write class for vCard and I give you some hints how to output those things in your web applications. These tips may be also useful for desktop applications.
I consider vCard as data transfer object(DTO). To make it simpler to use I use only properties of simple type so it is also possible to use them over web services. You can also use vCard as data contract between your application and domain model. How you implement vCard is up to you, I just help you get started.
vCard class
My vCard class is simple. It contains some properties for vCard data and one method that outputs vCard in vCard format. I try to keep this code short, so forgive me father for all documentation and other missing stuff and things implemented as dirty code.
public class VCard
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Organization { get; set; }
public string JobTitle { get; set; }
public string StreetAddress { get; set; }
public string Zip { get; set; }
public string City { get; set; }
public string CountryName { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
public string HomePage { get; set; }
public byte[] Image { get; set; }
public override string ToString()
{
var builder = new StringBuilder();
builder.AppendLine("BEGIN:VCARD");
builder.AppendLine("VERSION:2.1");
// Name
builder.AppendLine("N:" + LastName + ";" + FirstName);
// Full name
builder.AppendLine("FN:" + FirstName + " " + LastName);
// Address
builder.Append("ADR;HOME;PREF:;;");
builder.Append(StreetAddress + ";");
builder.Append(City + ";;");
builder.Append(Zip + ";");
builder.AppendLine(CountryName);
// Other data
builder.AppendLine("ORG:" + Organization);
builder.AppendLine("TITLE:" + JobTitle);
builder.AppendLine("TEL;HOME;VOICE:" + Phone);
builder.AppendLine("TEL;CELL;VOICE:" + Mobile);
builder.AppendLine("URL;" + HomePage);
builder.AppendLine("EMAIL;PREF;INTERNET:" + Email);
builder.AppendLine("END:VCARD");
return builder.ToString();
}
}
Using ToString() may be not a very good idea if you need to reserve default representation of vCard for user-interface output. Of course, there may be other considerations. You can rename this method to something else if you don’t want to use ToString() as name of method.
Adding image to vCard
We have basic vCard now but we have no support for images. Let’s update VCard class so our friends can more easily match our name and face. It is specially useful for dudes who sit too much at computer and doesn’t meet their friends very often.
vCard may contain image in it. Of course, image must be encoded to text. Base64 encoding works well for us. I suppose you have code that outputs image as byte array. Of course, you can modify my code and use some other type to keep image data. I tried to keep VCard and its dependencies minimal.
// Add image
builder.AppendLine("PHOTO;ENCODING=BASE64;TYPE=JPEG:");
builder.AppendLine(Convert.ToBase64String(Image));
builder.AppendLine(string.Empty);
Add this code right before vCard line where VCARD:END is appended to builder. Note that after image we have to output two line breaks. Without them image is not shown.
Testing output
Now let’s test VCard. We need to find out if it outputs data correctly or not. My test method is simple. I create new vCard, load image to it and then write it on my local hard disc. As a next thing I check out what Outlook thinks about my vCard.
public void Run()
{
var myCard = new VCard
{
FirstName = "Gunnar",
LastName = "Peipman",
Organization = "Developers Team Ltd",
JobTitle = "n00b",
StreetAddress = "Tööstuse 48",
City = "Tallinn",
CountryName = "Estonia",
Phone = "343-23232",
Mobile = "232-67854",
Email = "gunnar@secret-address.com",
HomePage = "www.developers-team.com"
};
myCard.Image = File.ReadAllBytes("C:\\Images\\me.jpg");
using (var file = File.OpenWrite("C:\\Files\\me.vcf"))
using (var writer = new StreamWriter(file))
{
writer.Write(myCard.ToString());
}
}
This time I am lucky. Okay, almost lucky. Outlook 2007 shows almost everything fine.The only exception is StreetAddress. We can see there characters that doesn’t match in UTF-8 and Windows-1257. But let’s solve this problem in next section.
Downloading vCards
Downloading vCard is not complex thing to implement. I found something weird here: Outlook 2007 was not able to read UTF-8 format vCards. I had to convert vCard to Windows-1257 to get Outlook 2007 reading them. Let’s see now how to download vCard from your web application.
Response.Clear();
Response.ContentType = "text/vcard";
var cardString = myCard.ToString();
var inputEncoding = Encoding.Default;
var outputEncoding = Encoding.GetEncoding("windows-1257");
var cardBytes = inputEncoding.GetBytes(cardString);
var outputBytes = Encoding.Convert(inputEncoding,
outputEncoding, cardBytes);
Response.OutputStream.Write(outputBytes, 0, outputBytes.Length);
Response.End();
Before writing vCard to response stream we clean response buffer so it is empty and contains no surprises. We also convert card output to Windows-1257 character set so Outlook is able to understand it. We output vCard as byte array because in this case it is sure that web server cannot modify string encoding.
So, that’s it guys. Our vCards should work now as expected and if you move VCard class to some common library you are able to use it in more than one application.
View Comments (8)
Great post.
Just one comment on the code. I think the purpose of using StringBuilder is to avoid string concatenation. Here you are using a StringBuilder object but still doing a concate:
builder.AppendLine("N:"+ LastName + ";" + FirstName);
Something like this would be better:
builder.AppendLine("N:");
builder.AppendLine(LastName);
builder.AppendLine(";");
builder.AppendLine(FirstName);
Thanks Shuiab, you are correct. YOU should do something like this. I just tried to save some space so my blog entry is not too long to scroll. But you got the point. :)
builder.AppendLine("URL;" + HomePage);
Just out of logical thinking I'm wondering if that line is correct. I expected it to read:
builder.AppendLine("URL:" + HomePage);
Cheers,
Wes
V Card is a God send. It works with .net perfectly.
Thanks for putting this use full article but my problem is that i want to export all data of a grid in vCard Format. Will you please help me for solving this
my code is:-
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.Charset = "";
HttpContext.Current.Response.ContentType = "text/x-vCard";
string vfilename = "MyContact.VCF";
HttpContext.Current.Response.AddHeader("content-disposition", "inline;filename=" + vfilename);
StringBuilder strHtmlContent = new StringBuilder();
System.IO.StringWriter stringWrite = new System.IO.StringWriter();
System.Web.UI.HtmlTextWriter htmlWrite = new HtmlTextWriter(stringWrite);
stringWrite.WriteLine("BEGIN:VCARD");
stringWrite.WriteLine("VERSION:2.1");
for (int i = 0; i < DL_Contact.Items.Count; i++) { CheckBox cb = (CheckBox)DL_Contact.Items[i].FindControl("chk_conchk"); if (cb.Checked == true) { Label lid = (Label)DL_Contact.Items[i].FindControl("lbl_id"); x = Convert.ToInt32(lid.Text); Objref = DA_SBP.DA_Group.usp_exportcontact(x); foreach (DataRow rand in this.Objref.Tables["table"].Rows) { stringWrite.WriteLine("N:" + vname + ";" + vmail + ";" + vcno); vmail = rand["EmailId"].ToString(); vname = rand["FullName"].ToString(); vcno = rand["PhoneNo"].ToString(); vcomp = rand["CompanyName"].ToString(); vjobtitle = rand["JobTitle"].ToString(); vadd = rand["Address1"].ToString(); stringWrite.WriteLine("FN:" + vname); stringWrite.WriteLine("Email:" + vmail); stringWrite.WriteLine("ORG:" + vcomp); stringWrite.WriteLine("TITLE:" + vjobtitle); stringWrite.WriteLine("ADR;WORK;ENCODING=QUOTED-PRINTABLE:" + vadd); } } } stringWrite.WriteLine("END:VCARD"); HttpContext.Current.Response.Write(stringWrite.ToString()); HttpContext.Current.Response.End(); But it giving me only last record. Please Solve this...
very nice example, but, how can i do to read the card, for example from a vb.net app and withouth looping in the action of read image from this vbcard =)
i hope u can help me
thanks =)
Thanks for question, Chris! :)
You have to write Parse() method to this class that takes string with vCard as input and then extracts information from it. Image is given as base64 encoded string. There are methods available in .NET framework to encode and decode base64 strings.
nice code love it