Python / Django - Vcard Photo

2k views Asked by At

I'm trying to generate a vcard using the vobject library but I am running into a couple of issues.

First off, I've tried adding a photo using both a url and base64 data, but I cannot get the photo to display on the OS X contacts app (Mavericks), or a Galaxy S4 running Android 4.4.2 . I even tried this on my Lumia 1520 running windows phone 8.1, but to no avail.

The code is as follows:

card = vobject.vCard()

attr = card.add('n')
attr.value = vobject.vcard.Name(family=agent.last_name, given=agent.first_name)

attr = card.add('fn')
attr.value = agent.get_full_name()

attr = card.add('email')
attr.value = agent.email_address
attr.type_param = 'INTERNET'

attr = card.add('tel')
attr.value = agent.cell_number
attr.type_param = 'cell'

attr = card.add('tel')
attr.value = branch.get_telephone_number()
attr.type_param = 'work'

attr = card.add('org')
attr.value = "Moe's Co"

photo_url = "http://www.abcrealestate.co.za/resize/100/150/uploads/agents/2012/03/testagent.jpg"
f = urllib.urlopen(photo_url)
data = f.read()
f.close()

attr = card.add('photo')
attr.type_param = 'JPEG'
attr.value = photo_url

response = HttpResponse(mimetype='text/x-vcard')
response['Content-Disposition'] = 'attachment; filename="%s.vcf"' % agent.get_full_name()
response.write(card.serialize())

return response

This generates the following file:

BEGIN:VCARD
VERSION:3.0
EMAIL;TYPE=INTERNET:[email protected]
FN:Abad Muhammed
N:Muhammed;Abad
ORG:M;o;e;';s; ;C;o
PHOTO;TYPE=JPEG:http://www.abcrealestate.co.za/resize/100/150/uploads/agents/2012/03/testagent.jpg
TEL;TYPE=cell:012 345 6789
TEL;TYPE=work:012 345 6789
END:VCARD

The above method places the URL of the image as the photo. The code for the base 64 implementation (see here for more info) is shown below:

photo_url = "http://www.abcrealestate.co.za/resize/100/150/uploads/agents/2012/03/testagent.jpg"
f = urllib.urlopen(photo_url)
data = f.read()
f.close()

attr = vcard.add('photo')
attr.type_param = 'jpeg'
attr.encoding_param = 'b'
attr.value = base64.encodestring(data)

The result:

BEGIN:VCARD
VERSION:3.0
EMAIL;TYPE=INTERNET:[email protected]
FN:Abad Muhammed
N:Muhammed;Abad
ORG:M;o;e;';s; ;C;o
PHOTO;TYPE=jpeg;ENCODING=b:LzlqLzRBQVFTa1pKUmdBQkFRQUFBUUFCQUFELzJ3QkRBQU1DQWdNQ0FnTURBd01FQXdNRUJRZ0ZCUVFFQlFvSEJ3WUlEQW9NREFzSwpDd3NORGhJUURRNFJEZ3NMRUJZUUVSTVVGUlVWREE4WEdCWVVHQklVRlJULzJ3QkRBUU1FQkFVRUJRa0ZCUWtVRFFzTkZCUVVGQlFVCkZCUVVGQlFVRkJRVUZCUVVGQlFVRkJRVUZCUVVGQlFVRkJRVUZCUVVGQlFVRkJRVUZCUVVGQlFVRkJUL3dBQVJDQUNXQUdRREFTSUEKQWhFQkF4RUIvOFFBSHdBQUFRVUJBUUVCQVFFQUFBQUFBQUFBQUFFQ0F3UUZCZ2NJQ1FvTC84UUF0UkFBQWdFREF3SUVBd1VGQkFRQQpBQUY5QVFJREFBUVJCUkloTVVFR0UxRmhCeUp4RkRLQmthRUlJMEt4d1JWUzBmQWtNMkp5Z2drS0ZoY1lHUm9sSmljb0tTbzBOVFkzCk9EazZRMFJGUmtkSVNVcFRWRlZXVjFoWldtTmtaV1puYUdscWMzUjFkbmQ0ZVhxRGhJV0doNGlKaXBLVGxKV1dsNWlabXFLanBLV20KcDZpcHFyS3p0TFcydDdpNXVzTER4TVhHeDhqSnl0TFQxTlhXMTlqWjJ1SGk0K1RsNXVmbzZlcng4dlAwOWZiMytQbjYvOFFBSHdFQQpBd0VCQVFFQkFRRUJBUUFBQUFBQUFBRUNBd1FGQmdjSUNRb0wvOFFBdFJFQUFnRUNCQVFEQkFjRkJBUUFBUUozQUFFQ0F4RUVCU0V4CkJoSkJVUWRoY1JNaU1vRUlGRUtSb2JIQkNTTXpVdkFWWW5MUkNoWWtOT0VsOFJjWUdSb21KeWdwS2pVMk56ZzVPa05FUlVaSFNFbEsKVTFSVlZsZFlXVnBqWkdWbVoyaHBhbk4wZFhaM2VIbDZnb09FaFlhSGlJbUtrcE9VbFphWG1KbWFvcU9rcGFhbnFLbXFzck8wdGJhMwp1TG02d3NQRXhjYkh5TW5LMHRQVTFkYlgyTm5hNHVQazVlYm42T25xOHZQMDlmYjMrUG42LzlvQURBTUJBQUlSQXhFQVB3RDlVNktLCktBQ2lpaWdBb29vb0FLS0thN2hGSlBTZ0NscWVxMnVpMkU5N2V6cGJXc0s3NUpwRGhWWDFyNXUrSW43Ym1qZUVneDA3dy9xMTdiSzIKMXRRdUxkb1lWLzJ0djMyWDhLNWY5b2Y0NVJhOVBQb3R1WWY3RWdrMnFWdUZXVzZrWCtKVi91ci9BQTE4amVLdkRYanJ4QTAxMXBxeQp5MmJmSzBlcGJmM2kvd0M4dTFxK1F4bWEvdlBaMHBXUjlYZ2NubFZoN1NwRSszZkJYN1oxbHJWeTBPcFd0dWpLcXliWTVOdjd0djhBCmxvck44ckwvQU44MTlDZUV2R0dsZU05TkY3cFZ5bHhGOTFnUHZJMzkxcS9HcXgwbnhsNFhhMWt2OU5sbGtzMXVMZUh5bTNOSkRNdisKcjNmM1ZiNXE5dCtDbjdTSGlUNFhhL2VGTk44K084a3Qxa2p1MlpZMlZWMnQ4MzhMZjNhTUxtVTR5NWFrdWFJOFpsWHU4MFljc2o5Ugp0dEZlUmVDZjJtL0JmaVhRWXIyOTFHRFFMb3NVa3N0UWxWWkZJNys0OUQzb3I2Tll1aS90SHpMdzlSYWNwNi9SUlJYWVloUlJSUUFVClVVVUFKMnhYbjN4eDhWbndYOE10YjFKRHRrRVloVnY3clNNSTkzL2oxZWduaXZNLzJpdkM5MTR5K0RIaXJUTE9Qekx5UzA4MkpDZnYKTkd5eUFmOEFqdGM5ZE9WS1hLYlVPWDJzZVk4RzhFL0NqVExTeHRieTVqYTZta1ZadDA3ZVp0WnZtYXVsMXpTTlBhejhsTGVOZHY4QQpzMWphVjR4azhJL0QzUjlRMVM3MHZUbS9zMk9abDFTU1RjMjJOZk1iYXRiMFhqblJkZDhGcjRoRXRxTGRsMnM4Yk50WnYrQmZOWDV5CjhMSGt1ZnA5TEVTNW94UEdQRjJoMnIzRExGdDNWNUw0ODA1YlBUWkxqeTFsamorV1JXL2lWcTlNOFFlTDRkWm04eGI3UWRQaG0zZloKZnRNMGl5WEczNzIzNWR2eTE1LzhTb3ByRHdmcWtseEdxK1hIdWJhMjVXWCs4clZ3VThQVW9WSTh4NjFXdEN0VGxGSExlSGZHK2sydQptSkhNdC9jT0Nja3RuYjdVVjRMYitLNzFwTGsyeXQ1Um1iSHkwVjczMVUrUDlvZnZwUlJSWDM1OElGRkZGQUJSUlJRQW1PS3p0ZWpWCjlIdmxmN3BnZmRqL0FIYTBxWTZCMEtrY1ZFbzNqWWNYeXl1ZUJhbm85dDloOHFXTzFXMlpmSi9mUjd0cS93QjFhODcrS0hoeExQd0sKZFB0YlRiYlNTZEZYNzFlZ2VQTEc4MHk2dTd1M3NKZFRldzgxb2JDR1JZMmtiK0hidStYZHQrN3VyenZ4cUwzeFA0ZFdWRnNZN2J5LwpNbWphL1dPYUZ2bCtWbGJiODFmQU9FcWlsSGwrRS9WY0ZKUGxtcEhtbmh2d2xEcTNodGJON1cydkxleDNSckJPdnpRN20rWmR2OE5ZCnZpVHd1dC9vdDFwYktxd3RENWFyL0N2L0FOalcxOE85U211dkVHcVc5cmEzTVZyWjIvbVhGOHpLMXRKdS93Q1dhdC9FeTBXcWZiOVMKdlBOL2VxdHF6TXYrMS9Ddi9mVmVXK2FWU05OL0VldlY1YWNaTkhnZGpvemVEMGxzSWRJRjFENWpPczdXVEV5WjRMWjc4ZzBWOXNmcwpqL0NQVWIvNFZUWGZqSFRiU2EvdU5WdW50V3VMV01uN0tDRmp4N2ZLMktLOXhZQ3M5ZVkrZi90bkF3OTNrMlByZWlpaXZ1ajh5Q2lpCmlnQW9vb29BS0tLS0FQT3ZpSGF4Mk43YjN3R3d6aG9wVDlQdXRYa0hqKzBzbXRacHJxd3Q1UTN5dDgzM3E5ZCtLdXQyTnJjYU5wTXoKcUwyOWVhU0dQKzhzY2Z6ZitoTFhoM2pmNGVXZXBMRzFwZFh6WEZ4OTIyaWszZk5YeEdZWGhYbjdNKzh5YXE2ZEdNcEhqWGlMeHZZKwpITEc0dDdObGlhVDkzdFgrN1QvZ0g1dmo3eDVwMXUwTHJveTNNYzEvZHQ5MWxWdHl4LzhBQW0yN3E3WFRmMlhiTkcrM2VJN2hwZjR2CnNrYmJWWC9lYXZFLzJwdjJzOUwrQlduemVBL2h1dHN2aVpvOXR4ZHdLclI2V3JmK2hUZitnL2VhcXkvS2FrcFJyVmZkRE1zNXB1TXEKTkxXWDRINmxSdytUR2tjUVNORkdBb0hTaXZ5SStBUC9BQVZJOFFmQ3I0YVdIaGp4RDRmdVBHVnhZdTZXK3BTM3pwTjVKd1ZqbEpCMwpzcExmTi9kMmp0UlgyVmo0TS9YNmlpaXJBS0tLS0FDaWl2UGZpSjhlZmg5OEtMbUsyOFdlTHRMMFc4bFhlbG5QTnVtWmY3M2xybHR2CisxdG9BOUNyejc0cmZIWHdIOEVORy90SHhwNGtzOURoWmN4d3l2dW5sLzY1d3JtUi93RGdLMThEZnRNZjhGU3IrNGl1dEUrRmxwSnAKa0xiby93RGhJTCtQZGNTRC9wakQ5MlAvQUhtM04vc3JYd2hkZUpkYThVYXRlYTU0aHZMbld0V3VOMGpYZW9UTk5NemY3VE5VQWZxago4WWZFSStPSHhxK0ZzL2dUVnhGZmFIL3hNdFdzN3VHUlpMZXh1TFZtVnBGL3ZmZFhidStWcEYzYmE5OWgreTZYR3F3UnJGY05Hdm1UCi93QVRmN3JmM2ErY2ZnL2NMTDhmSWZGVnJHdjlrNnA0VGh1SkdXUGR0YVJZMmpWZjlyNzFlamZIVDR0YWI4S1BBTjk0MjFlR1NLeHMKMWFPM2czZnZMcWIvQUpaeHIvdk4vd0N6ZjNheGhocWJxU3E4dnZIWlBFMUhUalNVdmRQQy93QnZEOXFKdmhQNFpid3o0ZnV2SzhXYQpsR3F3dEg4eldzUDhVMys5L0N2KzF1YitHdnkxdVBNZHByeSttYWU0bWtacEdsYmN6TTMzbVpxNnJ4eDR3MXI0b2VOTlU4WGVJSnZQCjFUVXBtbVpWKzdHdjhNYS83S3I4cTFodzJ2MnE0V1psWHk0L3VzMzhUZjNxN0RqSUxiVG96Q3BmY1dORmF2bE5SUUIvU0xSUlJVQUYKRkZGQUhHZkZ2NGxhWDhIdmgzcmZqRFd2TmZUOUxoOHhvNFYzUEl4WUtpTC9BTHpNcTErRGZqVDRoNnQ4UlBHWGlMeFpyTncwK3FheApmU1RUU00zM1YzZktxLzdLL2RWZjdxMStoSC9CVHI5b3E0c0dzL2hWb2Q1NWZuUnJlYTk1ZjhVYmY2bTNiLzBZMy9iT3Z6QjFDOVd3CnM1R2IrRnBQL1FxamxBZGZOOW84UVJ4L3c3dk1yVzMvQUhWck5odDltcGVZMy9QUGJWeGJqWXpTTjkxZm1xK1VEOWJQMkE5TTFEVy8KMlovRE45cUxMY3pOY1hrTnJLMGZ6UjJjTXpSd3gvN1g4VzJ2ajcvZ294OGNCOFR2aXZINEgwZTQzZUZmQ0ROYnNJMitXZSsvNWJTZgo4Qi8xYS83cmYzcStzOUkrSmtmN012OEF3VHc4SjYxR3l4NnkzaCszaDAxRy9pdnJwV2tWditBN21rLzREWDVQelhFaitaTTdOUGRUCk16YnBHK2FSbSs4emYraFZZRk82VDdSSjlsVCs3KytiKzZ2OTMvZ1ZXRjJvdjkxZnVyVWNLYlY4dFczTXpicEpmN3pWUisyTGM2MU4KQ1A4QVUyc2FyLzIwYi83R3FBdE5ja241UHUwVWprQnVldEZBSDlKVkZGRlpBRll2aTN4UnAvZ3J3MXF1dmF2UDlsMHpUTGFTOHVwcwpaMnh4cnVhdHF2anIvZ3AzOFYwOEMvczVYUGg4TEtMenhWT3RoSE5IS0Y4bU5HV1NSbVg3eksyMVkvbC81NmZlb0EvTG40MmZFbTQrCkp2eE84U2VLcnhuV2JWcjZTNlZXL3dDV2NiTis3ai80REdxci93QUJyeUh4WmRMY1c5cnNiNzF3cXRXOWZYdjJoZm0rV1QrSmE0M3gKRlA4QTZQd3UzeTdoVy84QUhhc0R2cnB0alZSdkoyWFQ3eHY3c0xmK2cxWXZwZjNLeWYzbXJZK0Z1azJ2aWo0a2VHOUwxRnR1bXpYMApjbDgzOTIxai9mVGYrUTQ1S29ENlcvYnErSmJYODN3OStHTnZKNVduK0Q5QnMydkYzZjhBTDVKYXgvSzMvWE9OVi83K05YeW5KdWxYCnpQOEFWYmwycXJmd3JXMTQ4OFpYWHhFOGJhOTRtdmYrUGpXTDZiVUpsYitGWkpHWlkvOEFnUHlyL3VyWE56VHMrNXQxQUVjbDU5bFcKUmxWZmxXc1R3azhzdzFHOE9OczB2RGZ4VlBxQ3pYRUxSd0swczBueXF0VDZaWS8ySnBpVzd5TDVtNW1rWmY3MUFFenNkM0xVVlRlUwpNdDk5RFJWZ2YweVVVVVZ6Z1kzaW54UnBmZ3J3M3FXdmF6ZHg2ZnBPbXdTWFYxY3ljTEhHbzNNMWZqRjhWdmlMcXY3Yy93QzByWjJTCjZoQm9PbTN6U2FicE1kOXVhTzF0MVZtWGNxL2VrazI3bS8ybTIvdzE5Sy84RlN2Mm43ZTN0WXZoRDRldTJudW04dTg4UU5iTnU4dGYKdlEycmY3VGZMSXkvM2ZML0FMMWZudjhBQ09LenYvaTE0Wi90ZldydnducHYyeFpwdGJnaithMVpWM0xJck44djNsVmYrQlZFdmhOYQpLaktwR01qN08rSS83UHZnRWFwNEw4VHA0SHRsMGZ3M0l0bjRtMHV5M0xIY1JySHRhUmxYNXBHVnRzMzk1bDNWOGhmdGFmQStMNFk2Ci93RDJwb051emVEdFkyeldNcXllWXNmOFNydS91c3JmTFgzemZmRi93YkZwUDJyVC9FV21UcnFFZjJYVU50d3NpK1p1K1c0LzNkemYKOTh0L3MxOC9lT3J6U2Izd0g0bCtIMnU2NVlwQmEvNlpvc3ZtTElza01uemVTckx1L3dCWEo4eS83TEwvQUhhK1pvNHF2Q3I3K3g5OQppc0ZoS3RCOG5MSHpQa1pyenovRGxyTi9zeDFzZkRtNmExdC9GV3JLMjM3UHBiV01iZjNaTHFSWWYvUmYyaXVlc2RFdjQ5RG5zR1NNCk9yTXNaOHhmbSthdm83NFZmc2RlTy9GSHdoZTN0L3NHbVgydFhkdnFoKzNUTnUreHJESXNMTXFxMjNkNWtqS3JmZVhhMzNXV3ZxS3QKV25SajdTcExsaWZBd3BUcXk5blRqek0rZHRRdjlza2NLZkxKY2Z2Ry93QjMrSC94Mm9kVXY0N0NGVjNmTlgxWGUvOEFCT3p4SzJycApkUytMZE9VcXZLcFp6Ti83TXRZdmpIOWlmVGZBV2c2ajRpOFplTGI2NnNiVlYvMGJSOVBWWkpHWnRxcnVrWnR2ek45N2JYbkxOTUhLClhMR1Y1Zk05TCt5Y1hHUE5LUHUrcVBtN1E3eFpkMXh0WmwrN3VYK0dyRjFjUjd0ejBsM2FRNlVqUVdFYnhXNVptK1krWTMvQW1xZ3IKeHY4QU16SzFldWVRT2E4WFB5cTJLS2dlZDkzeXEyS0tzRCtuQ3VOK0xueERzZmhOOE12RXZqSFVCdXROR3NaYnhrSC9BQzBaVitWZgorQk50WC9nVmRsWGduN2RYaGU3OFcvc2xmRXl4c2QvMmlQUy90bTJQK0pZSkZtWmYrK1kycm5BL0RYWFBFZDU0ajhSYWhyV28zRWx6CnFWOWNTWFZ4T3pibWtta2JkSXpmN1RNMVZadFVrVmY3ek4vRC9lckprdWxWV2JkOHRReDM3UmZNcXEwemZkLzJhMUExR0xiVzgxbGEKNGtYL0FJREd0VFd0MTluaFdHTDVZVi9oL3ZWaHgzVFB1K2JlMzhUZjNxc1EzUzdmdkx1cFdRN3M5ci9aaitHY2Z4dCtPUGhYd25jZgo4Z3U0dUd1dFJaZitmT0ZmTW0vNzZWZHYvQXEvUXY0SitJWlBFdmcvVVBGRjE1Y0UydGFoY2FoNUMvZGpqWnRzTWEvM1ZXRlkxWC9kCnI0Ni80SjRlTWZEL0FJQytMK3BlSVBFT29XZGpicnBzbW54L2FabGozZWF5N3R1Ny9aajIvd0RBcTVuNG5mR0RXdmhIZDZwNEMwSHgKQnArczIwYzBqV04zYlNlWkg5bFptOHZ6R1g1VmsyL0t5MTRXYVlhdGlveGpUUGZ5bkUwTU5LY3FwK2d1cGVJSVpXVm9HYVZXKzd0Kwphdmp2OXJEOXBQVDViVzY4RjZIYjZmck1kMURKRHF6U1NTSzF1Mjc1VmpaZmwzSzN6ZnhmZDIxODNQOEF0QmZFT1d6azBtNDE2U0NHCmFML1dXMzd0djlwZHkvdzE1eGVYRTNtTXpTYldiK0pmdXRYUGc4bjluVTlwVk9ySFp4N1NuN09nTnV0THQvTWE0dFZhS1RkOTNjeTEKSHFFdXhvMmpadDIzNWxhbS9OdC85bHFuY2RLK3BQbFJmdFVuL1BScUtvbDZLeTVnUDZrcW82bHA5dHFtbjNkamRRclBhWEVaaG5oYgo3cnhzTnJLZndvb3FBUDUxdjJpL2h2RjhIL2poNHk4RTI5eDlwczlGMU9XQzNrSSs5RnkwZTcvYThzaFc5eFhtMGtoMk1lN2ZMUlJRCkJQQ3lydFhiV3JONGowZGRIYTNqMGtyZHRHc1lueU9HL3ZVVVZyRURsZFcxaG5tYU9KZHErOVhORmtTR3lCU01lYS9WNktLVWZpQWYKUGNlZnNWOG5MZkkvOFMxSC9hakxONU1xN20vdkxSUlRBY1pWQzhMVks3a0xMUlJSSUNsUlJSV1FILy9aCg==
TEL;TYPE=cell:012 345 6789
TEL;TYPE=work:012 345 6789
END:VCARD

Apart from the photo not showing in both cases, I've also noticed that the company name which as set to "Moe's Co" has semi colons between each character in the output files.

Can anyone point advise me as to what I have done wrong ? Any advice would be appreciated.

Thanks in advance.

3

There are 3 answers

0
davidjb On

In the second situation, you've double-encoded the Base64 data. You don't need to use base64.encodestring on data because the vobject code does this for you upon setting encoding_param = 'b'. This wasn't obvious until I looked at the source to discover what was happening.

As for your first situation with the URL-based approach, I'm yet to see a client (Evolution on Ubuntu, Outlook 2010 on Win 7) work correctly with that in a vCard. Your clients are ignoring the URL, as are mine.

The vCard implementations summary at http://microformats.org/wiki/vcard-implementations gives an idea as to the quirks in different applications, though it isn't complete.

0
Bhargav Srinivasan On

To obtain the image data, use the requests library instead. Like @davidjb mentioned, you don't need to encode it into base64.

import requests
attr.value = requests.get(photo_url).content
0
Peter Norton On

For the company name, the value should be an array (so that you can have a department within a company for example). So just wrap the company name in brackets:

attr = card.add('org')
attr.value = ["Moe's Co"]