2

我正在尝试编写一个 MVC3 应用程序,该应用程序动态构建一个 vCard(.vcf) 文件,供人们在扫描 QR 码时在移动设备上下载。我在这里找到了一个示例来构建和返回 vCard。经过一些操作后,我能够让它在我的台式机上运行,​​但每次我尝试在移动设备上下载文件时,下载都会失败。我曾认为它发送的标头有问题,所以我做了一些挖掘并在此处找到了一些有关 android 的 HTTP 标头的有用信息。不幸的是,即使调整标题也没有改变任何东西。

第一部分是控制器,稍后将采用 URL 参数。保持简单的测试。

public class APIController : Controller
{

public vCardResult vCard()
{
    vCard card = new vCard
    {
        FirstName = "First",
        LastName = "Last",
        StreetAddress = "70 Street Adr.",
        City = "Atlanta",
        State = "GA",
        CountryName = "United States",
        Mobile = "5558675309",
        Organization = "MyCompany",
        HomePage = "www.Google.com",
        JobTitle = "Software Developer",
        Zip = "30318",
        Email = "FirstLast@MyCompany.com",
    };

    return new vCardResult(card);
}

这是 vCard 模型

public class vCard
{
    //I cut out the properties to save space

    public override string ToString()
    {

        var builder = new StringBuilder();

        builder.AppendLine("BEGIN:VCARD");
        builder.AppendLine("VERSION:2.1");
        builder.AppendLine("FN:" + FirstName + " " + LastName);
        builder.AppendLine("N:" + LastName + ";" + FirstName);
        builder.AppendLine("TEL;CELL:" + Mobile);
        builder.AppendLine("TEL:");
        builder.AppendLine("EMAIL;INTERNET:" + Email);
        builder.AppendLine("TEL;FAX:");
        builder.AppendLine("TITLE:" + JobTitle);
        builder.AppendLine("ORG:" + Organization);
        builder.AppendLine("ADR:;;" + StreetAddress + ";" + City + ";" + ";" + Zip + ";");
        builder.AppendLine("REV:20120730T15034z");
        builder.AppendLine("END:VCARD");

        return builder.ToString();

    }
}

最后是动作结果

public class vCardResult : ActionResult
{
    private vCard _card;

    protected vCardResult() { }

    public vCardResult(vCard card)
    {
        _card = card;
    }

    public override void ExecuteResult(ControllerContext context)

    {
        var response = context.HttpContext.Response;
        response.ContentType = "text/vcard";
        response.AddHeader("Content-Disposition", "attachment; fileName=\"" + _card.FirstName + "_" + _card.LastName + ".VCF\"");

        var cardString = _card.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);
    }
}

与我之前列出的示例没有太大不同,但我希望在我走得更远之前至少能实现这个功能。

我检查了从服务器发送的响应以及类似工作系统(我无法访问源代码的系统)的响应,但我没有看到很多差异,所以我不确定他们的 vCard 为什么会打开手机的联系人列表添加为新联系人,我的下载失败。答复如下。

示例响应(工作):

HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Disposition: attachment; filename=5240000053568089.vcf
Content-Type: text/x-vcard
Transfer-Encoding: chunked
Date: Tue, 31 Jul 2012 21:18:31 GMT

110
BEGIN:VCARD
VERSION:2.1
FN:First Last
N:Last;First
TEL;CELL:5558675309
TEL:
EMAIL;INTERNET:FirstLast@MyCompany.com
TEL;FAX:
TITLE:Software Developer
ORG:MyCompany
ADR:;;70 Street Adr.;Atlanta;GA;30318;
REV:20120523T150346Z
END:VCARD

0

我的应用程序的响应

HTTP/1.1 200 OK
Cache-Control: private
Transfer-Encoding: chunked
Content-Type: text/vcard
Server: Microsoft-IIS/7.0
X-AspNetMvc-Version: 3.0
Content-Disposition: attachment; fileName=First_Last.vcf
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Date: Tue, 31 Jul 2012 20:14:02 GMT

105
BEGIN:VCARD
VERSION:2.1
FN:First Last
N:Last;First
TEL;CELL:5558675309
TEL:
EMAIL;INTERNET:FirstLast@MyCompany.com
TEL;FAX:
TITLE:Software Developer
ORG:MyCompany
ADR:;;70 Street Adr.;Atlanta;GA;30346;
REV:20120730T15034z
END:VCARD

0

提前感谢您阅读本文以及您能够提供的任何帮助/建议!

4

5 回答 5

0
  • 首先需要将内容类型更改为:text/x-vcard
  • 然后我发现只有 ASP MVC FileContentResult 有效。因此,对于通过 asp 获取联系而不是重写 HTTP 标头的适当方式,只需使用上面的行: File(outputBytes, "text/x-vcard", Guid.NewGuid().ToString());
  • 第三不知道它是否 100% 正确,但您必须采取 GET 请求并包含“.vcf”扩展名,例如:somesite.com/ControllerName/ActionName.vcf

但是网络配置的标准设置不允许点所以

  • Forth 为您的配置添加一些改进

    <system.web>
    <httpHandlers> <add path=" .less" verb="GET" type="dotless.Core.LessCssHttpHandler, dotless.Core" /> <add path=".vcf " type="System.Web .Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" /> </httpHandlers> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true" /> <validation validateIntegratedModeConfiguration="false" /> <handlers> <add name="dotless" path=" .less" verb="GET" type="dotless.Core.LessCssHttpHandler,dotless.Core" resourceType="File" preCondition= "" /> <add name="ApiURIs-ISAPI-Integrated-4.0“路径=”.vcf" type="System.Web.Handlers.TransferRequestHandler" verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" preCondition="integratedMode,runtimeVersionv4.0" /> </handlers> </ system.web服务器>

这是通过 android 浏览器下载 vcard 的一些棘手方法,但它表示通过其他手机浏览器解决下载联系人更复杂

于 2013-06-16T13:29:00.900 回答
0

你从来没有关闭你的:

public class APIController : Controller 
于 2012-10-16T16:06:08.567 回答
0
using Maavak.Models;
using Maavak.Models.Vcard;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Diagnostics;
using System.Text;

namespace Maavak.Controllers
{
//[Route("api/[controller]")]
public class VCardTestController : Controller
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    public VCardTestController(
        IHttpContextAccessor httpContextAccessor
        )
    {
        _httpContextAccessor = httpContextAccessor;
        _vcard = new Vcard
        {
            FirstName = "First",
            LastName = "Last",
            StreetAddress = "70 Street Adr.",
            City = "Atlanta",
            State = "GA",
            CountryName = "United States",
            Mobile = "5558675309",
            Organization = "MyCompany",
            HomePage = "www.Google.com",
            JobTitle = "Software Developer",
            Zip = "30318",
            Email = "FirstLast@MyCompany.com",
        };
    }

    private readonly Vcard _vcard;

    [Route("vcardtest.vcf")]
    public IActionResult Get()
    {
        try
        {
            const string text_x_vcard = "text/x-vcard";
            HttpResponse response = _httpContextAccessor.HttpContext.Response;
            _httpContextAccessor.HttpContext.Response.ContentType = "text/vcard";
            _httpContextAccessor.HttpContext.Response.Headers.Add("Content-Disposition", "attachment; fileName=\"" + _vcard.FirstName + "_" + _vcard.LastName + ".VCF\"");

            string cardString = _vcard.ToString();
            Encoding inputEncoding = Encoding.Default;
            Encoding outputEncoding = Encoding.GetEncoding("windows-1255");
            byte[] cardBytes = inputEncoding.GetBytes(cardString);

            byte[] outputBytes = Encoding.Convert(inputEncoding,
                                    outputEncoding, cardBytes);

            return Content(cardString, text_x_vcard, Encoding.UTF8);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[{ ClsGlobal.GetCurrentMethod() }] { ex.Message } : { ex.InnerException } - {ex.StackTrace}");
            return NotFound();
        }
    }
}

}

这就是课堂

using System.Text;

namespace Maavak.Models.Vcard
{
public class Vcard
{
    public string FirstName { get; set; }
    public string CountryName { get; set; }
    public string LastName { get; set; }
    public string StreetAddress { get; set; }
    public string Organization { get; set; }
    public string Zip { get; set; }
    public string State { get; set; }
    public string City { get; set; }
    public string Mobile { get; set; }
    public string LandLine { get; set; }
    public string Fax { get; set; }
    public string HomePage { get; set; }
    public string Email { get; set; }
    public string JobTitle { get; set; }

    public override string ToString()
    {

        var builder = new StringBuilder();

        builder.AppendLine("BEGIN:VCARD");
        builder.AppendLine("VERSION:2.1");
        builder.AppendLine($"N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{ LastName };{ FirstName};;;;");
        builder.AppendLine($"FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{ FirstName } { LastName }");
        //builder.AppendLine("FN:" + FirstName + " " + LastName);
        //builder.AppendLine("N:" + LastName + ";" + FirstName);
        if (Mobile != null)
            builder.AppendLine("TEL;CELL:" + Mobile);
        if (LandLine != null)
            builder.AppendLine("TEL;MOME:" + LandLine);
        if (Email != null)
            builder.AppendLine("EMAIL;INTERNET:" + Email);
        if (Fax != null)
            builder.AppendLine("TEL;FAX:");
        if (JobTitle != null)
            builder.AppendLine("TITLE:" + JobTitle);
        if (Organization != null)
            builder.AppendLine("ORG:" + Organization);
        if (City != null)
            builder.AppendLine("ADR:;;" + StreetAddress + ";" + City + ";" + ";" + Zip + ";");
        //builder.AppendLine("REV:20120730T15034z");
        builder.AppendLine("END:VCARD");
        return builder.ToString();
    }

}

}

工作演示 在这里

于 2019-12-27T20:20:57.757 回答
0
using Maavak.Models;
using Maavak.Models.Vcard;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Diagnostics;
using System.Text;

namespace Maavak.Controllers
{
//[Route("api/[controller]")]
public class VCardController : Controller
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    public VCardController(
        IHttpContextAccessor httpContextAccessor
        )
    {
        _httpContextAccessor = httpContextAccessor;
    }


    [Route("{FirstName}-{LastName}-{Mobile}-{LandLine}-{Email}.vcf")]
    public IActionResult Get(
        string firstName,
        string lastName,
        string mobile,
        string landLine,
        string email
        )
    {
        try
        {
            Vcard _vcard = new Vcard()
            {
                FirstName = firstName,
                LastName = lastName,
                Mobile = mobile,
                LandLine = landLine,
                Email = email
            };
            const string text_x_vcard = "text/x-vcard";
            HttpResponse response = _httpContextAccessor.HttpContext.Response;
            _httpContextAccessor.HttpContext.Response.ContentType = "text/vcard";
            _httpContextAccessor.HttpContext.Response.Headers.Add("Content-Disposition", "attachment; fileName=\"" + _vcard.FirstName + " " + _vcard.LastName + ".VCF\"");

            return Content(_vcard.ToString(), text_x_vcard, Encoding.UTF8);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[{ ClsGlobal.GetCurrentMethod() }] { ex.Message } : { ex.InnerException } - {ex.StackTrace}");
            return NotFound();
        }
    }
}

}

然后像这样使用它

https://maavak.taboil.com/first-last-55555-66666-first@last.com.vcf

于 2019-12-27T21:14:34.093 回答
0

此解决方案适用于希伯来字符

using Maavak.Models;
using Maavak.Models.Vcard;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Diagnostics;
using System.Net;
using System.Text;

namespace Maavak.Controllers
{
//[Route("api/[controller]")]
public class VCardController : Controller
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    public VCardController(
        IHttpContextAccessor httpContextAccessor
        )
    {
        _httpContextAccessor = httpContextAccessor;
    }


    [Route("{FirstName}-{LastName}-{Mobile}-{LandLine}-{Email}.vcf")]
    public IActionResult Get(
        string firstName,
        string lastName,
        string mobile,
        string landLine,
        string email
        )
    {
        const string text_x_vcard = "text/x-vcard";
        try
        {
            Vcard _vcard = new Vcard()
            {
                FirstName = firstName,
                LastName = lastName,
                Mobile = mobile,
                LandLine = landLine,
                Email = email
            };

            _httpContextAccessor.HttpContext.Response.Headers.Add("Content-Disposition", "attachment; fileName=\"" +
                CleanNonAsciiForVcardHeadr(_vcard.FirstName) + " " +
                CleanNonAsciiForVcardHeadr(_vcard.LastName) + ".VCF\"");

            return Content(_vcard.ToString(), text_x_vcard, Encoding.UTF8);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[{ ClsGlobal.GetCurrentMethod() }] { ex.Message } : { ex.InnerException } - {ex.StackTrace}");
            return NotFound();
        }
    }
    private string CleanNonAsciiForVcardHeadr(string str)
    {
        return WebUtility.UrlEncode(str).Replace("+", "%20");
    }
}
}

课程是这样的

using System.Net;
using System.Text;

namespace Maavak.Models.Vcard
{
public class Vcard
{
    public string FirstName { get; set; }
    public string CountryName { get; set; }
    public string LastName { get; set; }
    public string StreetAddress { get; set; }
    public string Organization { get; set; }
    public string Zip { get; set; }
    public string State { get; set; }
    public string City { get; set; }
    public string Mobile { get; set; }
    public string LandLine { get; set; }
    public string Fax { get; set; }
    public string HomePage { get; set; }
    public string Email { get; set; }
    public string JobTitle { get; set; }

    public override string ToString()
    {

        var builder = new StringBuilder();
        string n = $"{ CleanNonAsciiForVcard(LastName) };{ CleanNonAsciiForVcard(FirstName) };;;";
        string fn = $"{ CleanNonAsciiForVcard(FirstName) } { CleanNonAsciiForVcard(LastName) }";
        builder.AppendLine("BEGIN:VCARD");
        builder.AppendLine("VERSION:2.1");
        builder.AppendLine($"N;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{ n }");
        builder.AppendLine($"FN;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:{ fn }");
        //builder.AppendLine("FN:" + FirstName + " " + LastName);
        //builder.AppendLine("N:" + LastName + ";" + FirstName);
        if (Mobile != null)
            builder.AppendLine("TEL;CELL:" + Mobile);
        if (LandLine != null)
            builder.AppendLine("TEL;HOME:" + LandLine);
        if (Email != null)
            builder.AppendLine("EMAIL;INTERNET:" + Email);
        if (Fax != null)
            builder.AppendLine("TEL;FAX:");
        if (JobTitle != null)
            builder.AppendLine("TITLE:" + JobTitle);
        if (Organization != null)
            builder.AppendLine("ORG:" + Organization);
        if (City != null)
            builder.AppendLine("ADR:;;" + StreetAddress + ";" + City + ";" + ";" + Zip + ";");
        //builder.AppendLine("REV:20120730T15034z");
        builder.AppendLine("END:VCARD");
        return builder.ToString();
    }
    private string CleanNonAsciiForVcard(string str)
    {
        return WebUtility.UrlEncode(str).Replace("+", "=20").Replace("%", "=");
    }
}
}

像这样使用它

https://maavak.taboil.com/first-last-55555-66666-first@last.com.vcf

或者

https://maavak.taboil.com/first-last-55555-%20-%20.vcf

https://maavak.taboil.com/עברית-עברית%20משפחה-55555-%20-%20.vcf

于 2019-12-27T22:40:02.293 回答