翻譯|使用教程|編輯:李顯亮|2020-11-03 09:59:45.900|閱讀 416 次
概述:在各種業務環境中,將各種文檔合并為一個PDF是客戶最常問的問題之一。本文演示了如何使用ASP.NET Core框架將多個文檔合并到一個PDF中。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
在各種業務環境中,將各種文檔合并為一個PDF是客戶最常問的問題之一。例如,假設您的組織有多個應用程序以XPS和PDF生成特定的文檔,使用掃描的圖像,并且您的用戶希望將其中一些文檔合并為一個PDF。
本文演示了如何使用ASP.NET Core框架將多個文檔合并到一個PDF中。Aspose.PDF提出了幾種使用.NET合并PDF的方法,這些內容在本文中進行了介紹。在本文中,將討論以下主題:
在本文中,我們將創建一個簡單的ASP.NET Web API應用程序,該應用程序允許我們上載文檔,選擇2個或更多文件進行合并以及下載結果。
 
 
	(安裝包僅提供部分功能,并設置限制,如需試用完整功能請。)
 
實施ASP.NET Core Web App以將各種文檔合并為PDF
步驟1:創建一個ASP.NET Core Web應用程序
我們將為此應用程序使用Web應用程序(模型-視圖-控制器)模板。
 
 
創建基本應用程序后,我們將需要執行一些其他操作。
"Folders": {
    "Files": "files",
    "Temporary" :  "temp" 
} 
 
	步驟2:實施Web API控制器以管理服務器上的文件
我們的控制器應執行以下操作:
using Aspose.Demo.Pdf.Merger.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Configuration;
namespace Aspose.Demo.Pdf.Merger.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class FilesController : ControllerBase
    {
        private readonly Dictionary<string, string> _contentType;
        private readonly ILogger<FilesController> _logger;
        private readonly string _storageRootFolder;
        public FilesController(ILogger<FilesController> logger,
            IWebHostEnvironment env,
            IConfiguration configuration)
        {
            _logger = logger;
            _storageRootFolder = Path.Combine(env.WebRootPath, configuration["Folders:Files"]);            
            _contentType = new Dictionary<string, string> {
            { ".txt", "text/plain"},
                { ".pdf", "application/pdf"},
                { ".doc", "application/vnd.ms-word"},
                { ".docx", "application/vnd.ms-word"},
                { ".xls", "application/vnd.ms-excel"},
                { ".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
                { ".png", "image/png"},
                { ".jpg", "image/jpeg"},
                { ".jpeg", "image/jpeg"},
                { ".gif", "image/gif"},
                { ".csv", "text/csv"}
            };
        }
        // GET: /api/files 
        [HttpGet]
        public IEnumerable<FileViewModel> GetFiles()
        {
            _logger.LogInformation($"Get files from {_storageRootFolder}");
            var files = new DirectoryInfo(_storageRootFolder).EnumerateFiles("*.pdf").ToList();
            files.AddRange(new DirectoryInfo(_storageRootFolder).EnumerateFiles("*.jpg"));
            files.AddRange(new DirectoryInfo(_storageRootFolder).EnumerateFiles("*.oxps"));
            //TODO: add other file types below            
            return files.Select(f => new FileViewModel { Name = f.Name, Size = f.Length });
        }
        [HttpGet("{id}")]
        public IActionResult OnGetFile(string id)
        {
            _logger.LogInformation($"Get file {id}");
            var fileName = Path.Combine(_storageRootFolder, id);
            return File(System.IO.File.OpenRead(fileName), _contentType[Path.GetExtension(fileName)]);
        }
        [HttpDelete("{id}")]
        public IActionResult OnDeleteFile(string id)
        {
            _logger.LogInformation($"Delete file {id}");
            var fileName = Path.Combine(_storageRootFolder, id);
            System.IO.File.Delete(fileName);
            return Ok();
        }        
    }
}
	然后將使用附加的庫Resumable.JS來加載文件,因此將與加載文件相關的代碼移至單獨的控制器是有意義的。
步驟3:實現Web API控制器以使用Resumable.JS上傳文件
Resumable.JS庫的主要功能是它允許您分塊加載文件。因此,我們需要實現一些方法來處理此過程:
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.IO;
using Microsoft.Extensions.Configuration;
namespace Aspose.Demo.Pdf.Merger.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UploadController : ControllerBase
    {
        private readonly ILogger_logger;
        private readonly string _storageRootFolder;
        private readonly string _filesRootFolder;
        public UploadController(
            ILoggerlogger,
            IConfiguration configuration,
            IWebHostEnvironment env)
        {
            _logger = logger;
            _storageRootFolder = Path.Combine(env.WebRootPath, configuration["Folders:Temporary"]);
            _filesRootFolder = Path.Combine(env.WebRootPath, configuration["Folders:Files"]);
            if (!Directory.Exists(_storageRootFolder))
                Directory.CreateDirectory(_storageRootFolder);
        }
        [HttpOptions]
        public object UploadFileOptions()
        {
            return Ok();
        }
        [HttpGet]
        public object Upload(int resumableChunkNumber, string resumableIdentifier)
        {
            _logger.LogInformation($"Check if chunck {resumableChunkNumber} from {resumableIdentifier} is here.");            
            return ChunkIsHere(resumableChunkNumber, resumableIdentifier) ? Ok() : StatusCode(418);
        }
        [HttpPost]
        public IActionResult Upload(
            [FromQuery(Name = "ResumableIdentifier")] string resumableIdentifier,
            [FromQuery(Name = "ResumableFilename")] string resumableFilename,
            [FromQuery(Name = "ResumableChunkNumber")] int resumableChunkNumber,
            [FromQuery(Name = "ResumableTotalChunks")] int resumableTotalChunks,
            IFormFile file)
        {
            _logger.LogInformation(file.FileName);
            var stream = System.IO.File.Create(GetChunkFileName(resumableChunkNumber, resumableIdentifier));
            file.CopyTo(stream);
            stream.Close();
            TryAssembleFile(resumableFilename, resumableIdentifier, resumableTotalChunks);
            return Ok();
        }
        #region Chunk methods
        [NonAction]
        private string GetChunkFileName(int chunkNumber, string identifier)
        {
            return Path.Combine(_storageRootFolder, $"{identifier}_{chunkNumber}");
        }
        [NonAction]
        private string GetFilePath(string identifier)
        {
            return Path.Combine(_storageRootFolder, identifier);
        }
        [NonAction]
        private bool ChunkIsHere(int chunkNumber, string identifier)
        {
            return System.IO.File.Exists(GetChunkFileName(chunkNumber, identifier));
        }
        [NonAction]
        private bool AllChunksAreHere(string identifier, int chunks)
        {
            for (var chunkNumber = 1; chunkNumber <= chunks; chunkNumber++) if (!ChunkIsHere(chunkNumber, identifier)) return false; return true; } [NonAction] private void DeleteChunks(string identifier, int chunks) { for (var chunkNumber = 1; chunkNumber <= chunks; chunkNumber++) { var chunkFileName = GetChunkFileName(chunkNumber, identifier); System.IO.File.Delete(chunkFileName); } } [NonAction] private string ConsolidateFile(string identifier, int chunks) { var path = GetFilePath(identifier); using var destStream = System.IO.File.Create(path, 15000); for (var chunkNumber = 1; chunkNumber <= chunks; chunkNumber++) { var chunkFileName = GetChunkFileName(chunkNumber, identifier); using var sourceStream = System.IO.File.OpenRead(chunkFileName); sourceStream.CopyTo(destStream); } destStream.Close(); return path; } [NonAction] private void TryAssembleFile(string rfn, string ri, int rtc) { if (AllChunksAreHere(ri, rtc)) { // Create a single file var path = ConsolidateFile(ri, rtc); // Move consolidated file System.IO.File.Move(path, Path.Combine(_filesRootFolder, rfn),true); // Delete chunk files DeleteChunks(ri, rtc); } } #endregion } }
	該庫將標識符用于內部目的。它可以以不同的方式生成。在示例應用程序中,我們使用了一個單獨的控制器。
using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
namespace Aspose.Demo.Pdf.Merger.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TokenController : ControllerBase
    {
        // GET: api/Token?id=<filename>
        [HttpGet("{id}")]
        public string OnGet(string id)
        {
            var hash = new System.Security.Cryptography.SHA1Managed()
                .ComputeHash(System.Text.Encoding.UTF8.GetBytes(id + DateTime.Now.Ticks.ToString()));
            return string.Concat(hash.Select(b => b.ToString("x2")));
        }
    }
}
步驟4:為合并的應用程序實現Web UI
現在,我們可以開始實現Web界面了。在示例應用程序中,我們沒有使用Angular,React Vue或其他框架,但是我們實現了基于Bootstrap和JQuery的單頁應用程序。應用程序頁面可以分為兩個部分:
由于該網頁的代碼量很大,因此在此不再顯示,我們將完全局限于描述該算法的兩個想法。
 
 
		以下代碼段演示了這兩種操作的處理程序:
let lastIndex = 0;
function selectFileClickHandler() {
    let order = parseInt($(this).attr('data-order'));
    if (order > 0) {
        $(this).attr('data-order', '0');
        $(this).find('span').hide('slow');
        for (let cell of $("*[data-order]")) {
            let currentOrder = parseInt(cell.dataset.order);
            if (currentOrder > order) {
                cell.dataset.order = currentOrder - 1;
                cell.firstElementChild.innerHTML = currentOrder - 1;
            }
        }
        lastIndex--;
    }
    else {
        $(this).attr('data-order', ++lastIndex);
        $(this).find('span').html(lastIndex);
        $(this).find('span').show('slow');
    }
    $('#btnMerge').prop('disabled', lastIndex<2);
}
$('#btnMerge').click((e) => {
    e.preventDefault();
    const files = $('*[data-order]').sort(function (a, b) {
        const contentA = parseInt($(a).data('order'));
        const contentB = parseInt($(b).data('order'));
        return (contentA < contentB) ? -1 : (contentA > contentB) ? 1 : 0;
    });
    const data = [];
    for (let file of files) {
        const currentOrder = parseInt(file.dataset.order);
        if (currentOrder > 0) data.push(file.dataset.id);
    }
    fetch('api/merge/',
        {
            method: 'POST',
            mode: 'cors',
            cache: 'no-cache',
            credentials: 'same-origin',
            headers: { 'Content-Type': 'application/json' },
            redirect: 'follow',
            referrerPolicy: 'no-referrer',
            body: JSON.stringify(data)
        }
    )
        .then(res => res.json())
        .then(res => {
            console.log(res);
            refreshFileTable();
        })
        .catch(err => alert(err));
    lastIndex = 0;
});
    
將各種文檔合并為PDF
完成準備階段后,我們可以考慮項目的主要部分。.NET庫的Aspose.PDF提供了幾種合并文檔的方法。您可以在上一篇文章中學習其中的一些內容,但是現在我們將重點介紹一下,并討論影響PDF中任何文檔的可能性。
實際上,如果文檔為PDF格式,那么我們必須執行兩個操作,然后合并;如果文檔不是PDF,則首先進行轉換然后合并。
步驟1:實施Web API控制器以將各種文檔合并為PDF
using Aspose.Pdf;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
namespace Aspose.Demo.Pdf.Merger.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class MergeController : ControllerBase
    {
        private readonly ILogger<MergeController> _logger;
        private readonly string _storageRootFolder;
        public MergeController(ILogger<MergeController> logger, IWebHostEnvironment env)
        {
            _logger = logger;
            _storageRootFolder = Path.Combine(env.WebRootPath, "files");
            //var license = new License();
            //license.SetLicense(@"<path to license>");
        }
        // POST: /api/merge 
        [HttpPost]
        public IActionResult PostMergeFiles(IEnumerable<string> list)
        {
            //TODO: Implement Image to PDF conversion
            throw new NotImplementedException();
        }
        
    }
}
如您所見,我們的控制器調用HTTP-Post方法來合并文檔。現在我們實現此方法。我們合并的想法是將所有頁面從一個文檔添加到另一個文檔。這很簡單,因為我們知道Document類包含一個Pages集合,而最后一個具有Add方法。
// POST: /api/merge 
        [HttpPost]
        public IActionResult PostMergeFiles(IEnumerable<string> list)
        {
            var document = new Document();
            foreach (var item in list)
            {
                var filePath = Path.Combine(_storageRootFolder, item);
                var pdfDocument = Path.GetExtension(item) switch
                {
                    ".jpg" => ConvertFromImage(filePath),
                    ".jpeg" => ConvertFromImage(filePath),
                    ".png" => ConvertFromImage(filePath),
                    ".oxps" => new Document(filePath, new XpsLoadOptions()),
                    _ => new Document(filePath)
                };
                document.Pages.Add(pdfDocument.Pages);
                pdfDocument.Dispose();
            }
            var guid = Guid.NewGuid();
            document.Save(Path.Combine(_storageRootFolder, $"{guid}.pdf"));
            _logger.LogInformation($"The merge result saved as: {guid}");
            return Ok(new { filename = guid.ToString() });
        }
        private Document ConvertFromImage(string filePath)
        {
            var docStream = new MemoryStream();
            var doc = new Document();
            var page = doc.Pages.Add();
            var image = new Aspose.Pdf.Image
            {
                ImageStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)
            };
            page.PageInfo.Margin.Bottom = 0;
            page.PageInfo.Margin.Top = 0;
            page.PageInfo.Margin.Left = 0;
            page.PageInfo.Margin.Right = 0;
            var imageSize = System.Drawing.Image.FromStream(image.ImageStream).Size;
            page.PageInfo.Width = imageSize.Width;
            page.PageInfo.Height = imageSize.Height;
            page.Paragraphs.Add(image);
            doc.Save(docStream);
            return doc;
        }
    }
步驟2:實現用于將圖像轉換為PDF的輔助方法
   private Document ConvertFromImage(string filePath)
        {
            var docStream = new MemoryStream();
            var doc = new Document();
            var page = doc.Pages.Add();
            var image = new Aspose.Pdf.Image
            {
                ImageStream = new FileStream(filePath, FileMode.Open, FileAccess.Read)
            };
            page.PageInfo.Margin.Bottom = 0;
            page.PageInfo.Margin.Top = 0;
            page.PageInfo.Margin.Left = 0;
            page.PageInfo.Margin.Right = 0;
            var imageSize = System.Drawing.Image.FromStream(image.ImageStream).Size;
            page.PageInfo.Width = imageSize.Width;
            page.PageInfo.Height = imageSize.Height;
            page.Paragraphs.Add(image);
            doc.Save(docStream);
            return doc;
        }
本文示例演示了Aspose.PDF庫在ASP.NET Core環境中的正常運行。該應用程序的目的是展示使用.NET Core的Aspose.PDF合并任何文檔并將其保存為PDF格式的可能性,并且可能需要對其進行改進。例如,此程序不考慮保存具有相同名稱的文件。該問題的可能解決方案是使用具有生成名稱的文件夾上載每個文檔或使用數據庫存儲文件。
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@ke049m.cn