移动设备上的MVC3 vCard下载失败

时间:2012-08-01 15:32:51

标签: asp.net-mvc asp.net-mvc-3

我正在尝试编写一个MVC3应用程序,该应用程序动态构建一个vCard(.vcf)文件,供移动设备上的人员扫描QR码时下载。我找到了一个示例here来构建和返回vCards。经过一些操作后,我能够在桌面计算机上运行它,但每次我尝试在移动设备上下载文件时,下载都会失败。我原以为它发送的标题有问题,所以我做了一些挖掘,发现了一些关于android here的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

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

5 个答案:

答案 0 :(得分:0)

你从未关闭过:

public class APIController : Controller 

答案 1 :(得分:0)

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

但是网络配置的标准设置不允许使用点

  • 为您添加一些补充资料

    <&的System.Web GT;
    < 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”/> <处理>   < add name =“dotless”path =“ .less”verb =“GET”type =“dotless.Core.LessCssHttpHandler,dotless.Core”resourceType =“File”preCondition =“”/>   < add name =“ApiURIs-ISAPI-Integrated-4.0”path =“ .vcf”type =“System.Web.Handlers.TransferRequestHandler”verb =“GET,HEAD,POST,DEBUG,PUT,DELETE, PATCH,OPTIONS“preCondition =”integratedMode,runtimeVersionv4.0“/> < /处理程序> < /system.webServer>

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

答案 2 :(得分: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();
    }

}

}

工作演示here

答案 3 :(得分: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

答案 4 :(得分: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