ASP.NET的临时文件下载链接

时间:2010-12-01 11:41:11

标签: .net asp.net url-rewriting

我想知道如何在有限的时间内为我的文件生成临时下载地址。我知道这不是最好的做法,可能使用HttpHandlers是根据http://www.devx.com/codemag/Article/34535/1954进行的方式。 但我很想知道如何使用urlrewriting来使用GUID或其他一些神秘的命名技术生成文件名,并使其在有限的时间内可用。
如果有人给我一篇关于它的好文章,我会很感激。

2 个答案:

答案 0 :(得分:6)

首先,您需要某种形式的标识符。您建议使用GUID并轻松完成,Guid.NewGuid().ToString("n")为您提供了这样的标识符。

你谈到URI重写,但这真的只是一点点修饰。您当然可以进行一些重写,将/myFiles/a948ec43e5b743548fd9a77c462b953e转换为/myFiles/download.aspx?id=a948ec43e5b743548fd9a77c462b953e,或者甚至(在查看查找表后)转换为myFiles/download.aspx?id=3myFiles/download.aspx?fileName=myNewDownload.pdf。这与任何其他URI重写任务相同,所以现在让我们忽略它并假设我们有一个请求进入/myFiles/download.aspx?id=a948ec43e5b743548fd9a77c462b953e,无论是否是由于重写。

好。你有一个标识符,你需要将它与三个匹配:流,内容类型和到期日。

您可以将所有这些存储在文件系统中,所有这些都存储在数据库中或数据库中的详细信息中,包括将流存储为文件系统中文件的路径。

让我们把它存储在文件系统中,其名称如下:

a948ec43e5b743548fd9a77c462b953e.application_pdf和a5d360178ec14e97abd556ed4b7709cf.text_plain; charset = utf-8

请注意,我们没有使用普通的Windows文件扩展名,因此我们处理上传机器与您的服务器绑定不同的情况。

如果a948ec43e5b743548fd9a77c462b953e是需要的项目,我们首先查看创建日期,如果它是很久以前(文件已过期),我们发送一个410 GONE标题,并显示一条错误消息,说明该文件已过期(我们可以此时还要删除文件以清理使用情况 - 或者截断它以使其保留文件曾经存在的记录,但是存储的是0字节。)

否则我们将Response.ContentType设置为“application / pdf”,然后设置Response.TransmitFile以发送文件。

如果我们以不同于文件的方式存储流,我们希望以小块(4096很好地匹配系统其他部分中的其他缓冲区)发送它,并且在它非常大的情况下定期致电Response.Flush()以防止内存问题。

这是你完成的基本系统。 Niceties将包括存储原始文件名并将其发送到内容处置标头中,并遵守Range请求,以便用户可以恢复失败的下载而不必从头开始。

所有这些都与用于确保只有正确的人拥有该文件的任何身份验证非常正交 - 您可以将其与任何类型的登录系统一起使用,或者您可以将其公开但有时间限制。

答案 1 :(得分:3)

无论您使用的是哪种ASP.NET框架,我都倾向于使用加密令牌来加密文件ID(我认为您已经拥有)和DateTime,它可以在服务器端解密并根据其时间戳进行验证。

这是一个简单的实现:

    public static string GetDownloadToken(int fileId)
    {
        byte[] idbytes = BitConverter.GetBytes(fileId); // 4 bytes 
        byte[] dateTimeBytes = BitConverter.GetBytes(DateTime.Now.ToBinary()); // 8 bytes
        byte[] buffer = new byte[16]; // minimum for an encryption block 
        string password = "password";

        byte[] passwordBytes = Encoding.ASCII.GetBytes(password);
        Array.Copy(idbytes, 0, buffer, 0, idbytes.Length);
        Array.Copy(dateTimeBytes, 0, buffer, idbytes.Length, dateTimeBytes.Length);
        byte[] encryptedBuffer = new byte[256]; 
        using (SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider())
        {
            int count = sha1.TransformBlock(buffer, 0, buffer.Length, encryptedBuffer, 0);
            return Convert.ToBase64String(encryptedBuffer, 0, count);
        }
    }