使用.NET Core下载pdf文件已损坏

时间:2017-05-30 13:47:54

标签: c# .net asp.net-core asp.net-core-mvc .net-core

我在不同的网站上发现了这段代码或类似的代码,在我的应用程序中,没有抛出任何错误,但下载的PDF文件,打开文件时已损坏,且只有5KB

文件的网址是:

" https://optionline-api-files.s3.amazonaws.com/pla592d774e504e8.pdf"

我用来下载的代码是:

[HttpPost]
        [Route("api/[controller]/UploadFileToAzureStorage")]
        public async Task<IActionResult> GetFile([FromBody]PDF urlPdf)
        {
            string localFilePath = await CreateTemporaryFile(urlPdf.urlPDF);

            // Create storage account
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageAccount);

            // Create a blob client.
            CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient();

            // Get a reference to a container named "mycontainer."
            CloudBlobContainer container = blobClient.GetContainerReference(UploaderStorage.Container);

            // Get a reference to a blob named "myblob".
            CloudBlockBlob blockBlob = container.GetBlockBlobReference("myblob");

            // Create or overwrite the "myblob" blob with the contents of a local file
            // named "myfile".
            using (var fileStream = System.IO.File.OpenRead(localFilePath))
            {
                await blockBlob.UploadFromStreamAsync(fileStream);
            }

            return Ok();
        }


        /// <summary>
        /// Creates temporary file
        /// </summary>
        /// <param name="urlPdf">PDF URL</param>
        /// <returns>Returns path of the new file</returns>
        private async Task<string> CreateTemporaryFile(string urlPdf)
        {
            Uri uri = new Uri(urlPdf);
            string filename = default(string);

            filename = System.IO.Path.GetFileName(uri.LocalPath);


            using (HttpClient client = new HttpClient())
            {
                using (HttpResponseMessage response = await client.GetAsync(urlPdf, HttpCompletionOption.ResponseHeadersRead))
                using (Stream streamToReadFrom = await response.Content.ReadAsStreamAsync())
                {
                    string fileToWriteTo = @"\\pc030\TemporaryPDF\"+filename;
                    using (Stream streamToWriteTo = System.IO.File.Open(fileToWriteTo, FileMode.Create))
                    {
                        await streamToReadFrom.CopyToAsync(streamToWriteTo);
                    }
                }
            }

            return   await Task.FromResult(@"\\pc030\TemporaryPDF\" + filename);

        }

1 个答案:

答案 0 :(得分:1)

您应该考虑一种解耦设计,这将使您的应用程序更易于维护和测试。

interface IStreamLoader
{
    Task<Stream> GetStreamAsync( Uri uri );
}

interface IStreamRepository
{
    Task<Stream> GetAsync( string id );
    Task PutAsync( string id, Stream stream );
    Task DeleteAsync( string id );
}

public class MyController
{
    private readonly IStreamLoader _streamLoader;
    private readonly IStreamRepository _streamRepository;

    public MyController( IStreamLoader streamLoader, IStreamRepository streamRepository )
    {
        _streamLoader = streamLoader;
        _streamRepository = streamRepository;
    }

    [Route("api/[controller]/UploadFileToAzureStorage")]
    public async Task<IActionResult> GetFile([FromBody]PDF urlPdf)
    {
        Uri pdfUri = new Uri( urlPDF.urlPDF );
        using ( var pdfStream = await _streamLoader.GetStreamAsync( pdfUri ) )
        {
            await _streamRepository.PutAsync( "myblob", pdfStream );
        }
        return Ok();
    }
}

很干净,不是吗?我们不再关心文件名,因为我们只想要一个流。

现在IStreamLoader实现了一个很好的功能:当我们关闭/处理流时,相关文件将被删除。这使临时目录保持清洁。

class StreamLoader : IStreamLoader
{
    private readonly string _tempPath;

    public StreamLoader()
    {

    }

    public StreamLoader( string tempPath )
    {
        _tempPath = tempPath;
    }

    private string GetTempFileName()
    {
        string filename;
        if ( _tempPath == null )
        {
            filename = Path.GetTempFileName();
        }
        else
        {
            filename = Path.Combine( _tempPath, Guid.NewGuid().ToString() );
            using ( File.Create( filename ) )
            { }
        }
        return filename;
    }

    public async Task<Stream> GetStreamAsync( Uri uri )
    {
        Stream result;
        using ( var client = new HttpClient() )
        {
            var response = await client.GetAsync( uri ).ConfigureAwait( false );
            response.EnsureSuccessStatusCode();

            var filename = GetTempFileName();

            using ( var stream = File.OpenWrite( filename ) )
            {
                await response.Content.CopyToAsync( stream );
            }
            result = new FileStream( 
                path: filename, 
                mode: FileMode.Open, 
                access: FileAccess.Read, 
                share: FileShare.None,
                bufferSize: 4096, 
                options: FileOptions.DeleteOnClose );
        }
        return result;
    }
}

最后,我们需要Azure的IStreamRepository实现:

class AzureStreamRepository : IStreamRepository
{
    private readonly CloudStorageAccount _storageAccount;
    private readonly string _containerName;

    public AzureStreamRepository( string connectionString, string containerName )
    {
        _storageAccount = CloudStorageAccount.Parse( connectionString );
        _containerName = containerName;
    }

    public async Task DeleteAsync( string id )
    {
        var blockBlob = GetBlockBlob( id );
        await blockBlob.DeleteAsync();
    }

    public async Task<Stream> GetAsync( string id )
    {
        var blockBlob = GetBlockBlob( id );
        Stream result = new MemoryStream();
        try
        {
            await blockBlob.DownloadToStreamAsync( result );
        }
        catch ( Exception )
        {
            result.Dispose();
            throw;
        }
        result.Seek( 0, SeekOrigin.Begin );
        return result;
    }

    public async Task PutAsync( string id, Stream stream )
    {
        var blockBlob = GetBlockBlob( id );
        await blockBlob.UploadFromStreamAsync( stream );
    }

    private Microsoft.WindowsAzure.Storage.Blob.CloudBlockBlob GetBlockBlob( string id )
    {
        var client = _storageAccount.CreateCloudBlobClient();
        var container = client.GetContainerReference( _containerName );
        return container.GetBlockBlobReference( id );
    }

}

您应该使用DI将实例注入控制器。

对于没有DI的快速测试,将此构造函数添加到控制器(StorageAccount看起来像控制器的conststatic string属性)

public class MyController
{
    public MyController() : this( 
        new StreamLoader( @"\\pc030\TemporaryPDF\" ),
        new AzureStreamRepository( StorageAccount, UploaderStorage.Container ) )
    {}
}