使用异步从MemoryStream中读取

时间:2013-10-04 16:19:33

标签: asp.net vb.net asynchronous

我使用iTextSharp(而不是相对于iTextSharp)使用类似于以下代码生成PDF字节:

Function GetFileBytes() as Byte()
Dim result() As Byte
    Using ms As New MemoryStream

        Dim reader As New PdfReader(pdfForm)
        Dim writer As New PdfStamper(reader, ms)

        Dim fields As AcroFields = writer.AcroFields

        fields.SetField("Customer Name", custInfo.FirstName + " " + custInfo.LastName)

        result = ms.GetBuffer
    End Using
End Function

我想让它成为异步任务,而是返回Task(Of Byte()),但是我在查明如何使我的内存流以异步模式写入时遇到问题,以及如何返回一个Task(Of) Byte())在这个过程中。有什么提示,建议吗?谢谢!

编辑1 我想澄清使用任务并使用此代码的必要性,因为到目前为止看起来有些评论是“为什么你这样做”和“不只是这样做,我想礼貌地说“谢谢,但我想这样做”:)

这是我的方案,想象一个网站收集几页上的数据,然后呈现一个评论屏幕,然后让用户能够查看包含该数据的PDF。在评论屏幕上,我需要生成一个1 MB的PDF文件,将其上传到BLOB存储,并在用户选择下载时使其可用。

我想在用户查看评论屏幕时生成并上传PDF,而不是在他们选择“查看PDF”按钮时。该代码将使用我的示例完成,这就是为什么我很好奇如何创建类型为Byte()的任务。任何帮助总是受到赞赏,再次感谢!

4 个答案:

答案 0 :(得分:0)

响应您的编辑:此方案有意义。在Task.Run来电中包裹所有内容。一定要是线程安全的(例如,不要在任务中使用HttpContext)。还记录错误,因为它们可能是错误。

请注意,使用ASP.NET后台工作可以随时中止(例如,当工作进程回收时)。你必须忍受突然消失的背景工作。在您的情况下,当用户尝试访问PDF时,可能会丢失PDF。此外,如果在用户尝试下载时仍然生成该怎么办。必须解决这些问题。

答案 1 :(得分:0)

如果你真的想做异步,一个更简单的方法就是在类中创建一个私有线程,page_init上的那个线程将启动进程然后在Page_LoadComplete上线程会等待使用thread.join()然后你会获得函数结果,但如上所述,相对速度增益最小,请考虑线程(或任务)非常昂贵

选项2
创建一个返回pdf文件数据的通用处理程序的ajax请求,这样就不会涉及任何线程和更好的用户体验

<%@ WebHandler Language="VB" Class="GetFile" %>

Imports System
Imports System.Web
Imports System.IO

Public Class GetFile : Implements IHttpHandler


    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest        
        context.Response.Clear()        
            If File.Exists(filePath & docName) Then
                context.Response.AddHeader("content-disposition", "attachment; filename=" & HttpContext.Current.Server.UrlEncode(docName))
                context.Response.ContentType = "text/pdf"
                context.Response.WriteFile(filelocation & docName) 'in your case this writes the bites of the generated file, so instead of the path just pass in the data
            End If 
        context.Response.Flush()
        context.Response.End()
    End Sub

    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
        Get
            Return False
        End Get
    End Property

End Class

答案 2 :(得分:0)

  

这是我的方案,想象一个网站收集几页上的数据,然后呈现一个评论屏幕,然后让用户能够查看包含该数据的PDF。在评论屏幕上,我需要生成一个1 MB的PDF文件,将其上传到BLOB存储,并在用户选择下载时使其可用。

由于您引用“blob存储”,我假设您使用的是Azure。

您需要工作人员角色或服务才能可靠地执行此操作。 worker角色应该只是从Azure队列中读取并生成PDF。

因此,当您的Web角色生成审阅屏幕时,在返回响应之前,它还会将该数据放入Azure队列中。当您的Web角色收到用户接受审核的通知时,它应该(异步)等待生成PDF文件。

答案 3 :(得分:-1)

(不要在下面使用此代码..请参阅&#34的结尾;回答&#34;进行推理)..

感谢大家的帮助。最后我接近了这个错误。我只需要创建一个Async包装函数来返回我的Task(Of Byte())

所以如果我的初始代码是这样的话:

Function GetFileBytes() as Byte()
    Dim result() As Byte

      Using ms As New MemoryStream

        Dim reader As New PdfReader(pdfForm)
        Dim writer As New PdfStamper(reader, ms)

        Dim fields As AcroFields = writer.AcroFields

        fields.SetField("Customer Name", custInfo.FirstName + " " + custInfo.LastName)

        result = ms.GetBuffer
    End Using
End Function

现在,我创建了一个名为GetFileBytesAsync的新方法,以返回调用上述相同GetFileBytes方法的Task(Of Byte()),它只是使用Task.Factory.StartNew调用,如下所示:

Public Shared Async Function GetFileBytesAsync() As Task(Of Byte())
        Dim t As Task(Of Byte()) = Task.Factory.StartNew(Function()
                                                             Return GetFileBytes()
                                                         End Function)

        Return Await t

    End Function

在我的调用函数中,我现在只使用我的任务对象的Result属性来访问字节数组。 HTH!

编辑:好的,正如Stephen Cleary恰当地指出的那样,我做错了,上面的代码不推荐,也不是异步做任何事情......事实上它是创造它的新线程来创建一些阻塞代码。遗憾...

我遇到了这个TechEd演讲:http://channel9.msdn.com/Events/TechEd/NorthAmerica/2013/DEV-B337 - 它是如何使用Async构建ASP.NET Web应用程序的。在54分钟标记处,它实际上表示尝试使用&#34;后台线程&#34;在ASP.NET Web服务器上是错误的,因为在IIS中没有后台线程可供使用,实质上你正在占用可以用于响应Web请求的线程。