Python / Django - Vcard Photo

Я пытаюсь сгенерировать vcard, используя библиотеку vobject, но я столкнулся с несколькими проблемами.

Прежде всего, я попытался добавить фотографию, используя как URL, так и данные base64, но не могу отобразить фотографию в приложении контактов OS X (Mavericks) или Galaxy S4 под управлением Android 4.4.2 . Я даже попробовал это на моем Lumia 1520 под управлением Windows Phone 8.1, но безрезультатно.

Код выглядит следующим образом:

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

Это создает следующий файл:

BEGIN:VCARD
VERSION:3.0
EMAIL;TYPE=INTERNET:test@example.com
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

Вышеуказанный метод размещает URL изображения как фотографию. Код для реализации base 64 (см. Здесь для получения дополнительной информации) показан ниже:

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)

Результат:

BEGIN:VCARD
VERSION:3.0
EMAIL;TYPE=INTERNET:test@example.com
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

Помимо фотографии, не показанной в обоих случаях, я также заметил, что название компании, которое установлено как "Moe's Co", содержит точки с запятой между каждым символом в выходных файлах.

Может ли кто-нибудь указать мне, что я сделал неправильно? Любой совет будет принят во внимание.

Заранее спасибо.

2 ответа

Во второй ситуации вы дважды кодировали данные Base64. Вам не нужно использовать base64.encodestring на data потому что код vobject делает это для вас при настройке encoding_param = 'b', Это не было очевидно, пока я не посмотрел на источник, чтобы узнать, что происходит.

Что касается вашей первой ситуации с подходом на основе URL, мне еще предстоит увидеть, как клиент (Evolution в Ubuntu, Outlook 2010 в Win 7) правильно работает с этим в vCard. Ваши клиенты игнорируют URL, как и мои.

Сводка реализаций vCard по адресу http://microformats.org/wiki/vcard-implementations дает представление об особенностях различных приложений, хотя и не является полной.

Чтобы получить данные изображения, используйте библиотеку запросов. Как упомянуто @davidjb, вам не нужно кодировать его в base64.

import requests
attr.value = requests.get(photo_url).content

Для названия компании значение должно быть массивом (чтобы вы могли иметь отдел внутри компании, например). Поэтому просто заключите название компании в скобки:

attr = card.add('org')
attr.value = ["Moe's Co"]
Другие вопросы по тегам