ASP .NET Core Import & Export A Word File
This guide explores how to import existing Word documents, display their content, and create documents from scratch using the IronWord library. By the end of this tutorial, you'll have created an ASP.NET Core web application that can:
- Upload and read Word documents
- Display the content of these documents in a textbox
- Export the Docx file
This project is perfect for developers who need to integrate Word document processing into their web applications, whether for document management systems, report generators, or any other scenario involving Microsoft Word files.
Prerequisites
To follow along with this tutorial, you should have:
- Basic knowledge of C# and ASP.NET Core
- Visual Studio 2019 or later installed (alternatively, you can use Visual Studio Code with the C# extension)
- .NET Core SDK 3.1 or later
Don't worry if you're not an expert in these technologies – we'll guide you through each step of the process!
What is IronWord?
IronWord is a .NET library that allows developers to programmatically read, manipulate, and create Microsoft Word documents. It provides a high-level API that simplifies working with Word files, making it an excellent choice for our project.
Some key features of IronWord include:
- Reading and writing various Word formats (DOCX, DOC, etc.)
- Manipulating document content and structure
- Formatting text and paragraphs
- Working with tables, images, and other document elements
- Mail Merge process for Documents
- Converting a Word document to a PDF document with ease, allowing you to take your final Word documents and make them easy-to-share PDF files
Now that we have an overview of what we're building and the tools we'll use, let's dive into setting up our project!
2. Setting up the project
In this section, we'll create a new ASP.NET Core project and install the necessary packages to work with IronWord.
2.1 Creating a new ASP.NET Core Project
- Open Visual Studio 2019 or later.
- Click on "Create a new project".
- Search for "ASP.NET Core Web Application" and select it.
- Click "Next".
- Name your project "WordDocumentProcessor" (or any name you prefer).
- Select the .NET Framework and a location for your project and click "Create".
2.2 Installing the IronWord NuGet Package
Now that we have our project set up, let's add the IronWord library:
- Right-click on your project in the Solution Explorer.
- Select "Manage NuGet Packages".
- In the "Browse" tab, search for "IronWord".
- Look for the official IronWord package.
- Click "Install" to add it to your project.
2.3 Updating the Existing Controller and View
Let's update our existing structure to incorporate the document processing functionality:
- We'll be using the existing HomeController.cs in the Controllers folder for our document processing logic.
- We'll update the existing Index.cshtml view in the Views/Home folder to include the document upload and display functionality.
Now that we have our project set up and the IronWord package installed, we're ready to start implementing the document import and export functionality. We'll be adding new methods to our HomeController and modifying the Index view to handle these features. In the next section, we'll focus on importing Word documents and displaying their content, utilizing our existing controller and view structure.
3. Importing Word Documents
In this section, we'll explore how to implement a feature for importing and processing Word documents in an ASP.NET MVC application. We'll cover both the user interface design and the back-end controller logic.
3.1 User Interface Design
The user interface for importing Word documents is designed to be intuitive and visually appealing. Let's break down the key components of the UI:
3.1.1 Upload Area
The upload area is the focal point of the interface, inviting users to select and upload their Word documents. Here's how it's structured:
<div class="upload-area">
<svg class="file-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<polyline points="10 9 9 9 8 9"></polyline>
</svg>
<p>Choose a Word document</p>
<label for="fileInput" class="choose-file">Choose File</label>
<p class="file-info">.DOC or .DOCX (MAX. 10MB)</p>
<button id="uploadBtn" class="upload-button">Upload and Process</button>
</div>
<div class="upload-area">
<svg class="file-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<polyline points="10 9 9 9 8 9"></polyline>
</svg>
<p>Choose a Word document</p>
<label for="fileInput" class="choose-file">Choose File</label>
<p class="file-info">.DOC or .DOCX (MAX. 10MB)</p>
<button id="uploadBtn" class="upload-button">Upload and Process</button>
</div>
This code creates a visually appealing upload area with a file icon, a hidden file input, and a styled label acting as the file selection button. It also includes information about accepted file types and a button to initiate the upload and processing.
3.1.2 Content Display Area
After processing the document, its content is displayed in a dedicated area:
<div class="content-wrapper">
<h2>Document Content:</h2>
<div id="documentContent" class="content-area">
No content to display.
</div>
</div>
<div class="content-wrapper">
<h2>Document Content:</h2>
<div id="documentContent" class="content-area">
No content to display.
</div>
</div>
This section provides a scrollable area to display the processed document content.
3.2 Controller Implementation
The HomeController handles the server-side logic for importing and processing Word documents. Let's examine the key methods:
3.2.1 UploadAndProcess Method
This method is responsible for handling the file upload and processing:
[HttpPost]
public IActionResult UploadAndProcess(IFormFile file)
{
if (file == null || file.Length == 0)
{
return Json(new { success = false, message = "No file uploaded." });
}
var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
if (fileExtension != ".doc" && fileExtension != ".docx")
{
return Json(new { success = false, message = "Invalid file type. Please upload a .doc or .docx file." });
}
try
{
var tempFilePath = Path.GetTempFileName();
using (var stream = new FileStream(tempFilePath, FileMode.Create))
{
file.CopyTo(stream);
}
StringBuilder contentBuilder = new StringBuilder();
WordDocument doc = new WordDocument(tempFilePath);
foreach (Paragraph paragraph in doc.Paragraphs)
{
foreach (Text textRun in paragraph.Texts)
{
contentBuilder.AppendLine(textRun.Text);
}
contentBuilder.AppendLine(); // Add an extra line between paragraphs
}
System.IO.File.Delete(tempFilePath); // Clean up the temporary file
return Json(new { success = true, content = FormatContentAsHtml(contentBuilder.ToString()) });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing document");
return Json(new { success = false, message = "An error occurred while processing the document." });
}
}
[HttpPost]
public IActionResult UploadAndProcess(IFormFile file)
{
if (file == null || file.Length == 0)
{
return Json(new { success = false, message = "No file uploaded." });
}
var fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant();
if (fileExtension != ".doc" && fileExtension != ".docx")
{
return Json(new { success = false, message = "Invalid file type. Please upload a .doc or .docx file." });
}
try
{
var tempFilePath = Path.GetTempFileName();
using (var stream = new FileStream(tempFilePath, FileMode.Create))
{
file.CopyTo(stream);
}
StringBuilder contentBuilder = new StringBuilder();
WordDocument doc = new WordDocument(tempFilePath);
foreach (Paragraph paragraph in doc.Paragraphs)
{
foreach (Text textRun in paragraph.Texts)
{
contentBuilder.AppendLine(textRun.Text);
}
contentBuilder.AppendLine(); // Add an extra line between paragraphs
}
System.IO.File.Delete(tempFilePath); // Clean up the temporary file
return Json(new { success = true, content = FormatContentAsHtml(contentBuilder.ToString()) });
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing document");
return Json(new { success = false, message = "An error occurred while processing the document." });
}
}
<HttpPost>
Public Function UploadAndProcess(ByVal file As IFormFile) As IActionResult
If file Is Nothing OrElse file.Length = 0 Then
Return Json(New With {
Key .success = False,
Key .message = "No file uploaded."
})
End If
Dim fileExtension = Path.GetExtension(file.FileName).ToLowerInvariant()
If fileExtension <> ".doc" AndAlso fileExtension <> ".docx" Then
Return Json(New With {
Key .success = False,
Key .message = "Invalid file type. Please upload a .doc or .docx file."
})
End If
Try
Dim tempFilePath = Path.GetTempFileName()
Using stream = New FileStream(tempFilePath, FileMode.Create)
file.CopyTo(stream)
End Using
Dim contentBuilder As New StringBuilder()
Dim doc As New WordDocument(tempFilePath)
For Each paragraph As Paragraph In doc.Paragraphs
For Each textRun As Text In paragraph.Texts
contentBuilder.AppendLine(textRun.Text)
Next textRun
contentBuilder.AppendLine() ' Add an extra line between paragraphs
Next paragraph
System.IO.File.Delete(tempFilePath) ' Clean up the temporary file
Return Json(New With {
Key .success = True,
Key .content = FormatContentAsHtml(contentBuilder.ToString())
})
Catch ex As Exception
_logger.LogError(ex, "Error processing document")
Return Json(New With {
Key .success = False,
Key .message = "An error occurred while processing the document."
})
End Try
End Function
This method performs the following tasks:
- Validates the uploaded file, ensuring it's in the correct file format (DOC or DOCX).
- Processes the document using the IronWord library.
- Returns the formatted content as JSON.
3.2.2 FormatContentAsHtml Method
This private method formats the extracted content into HTML:
private string FormatContentAsHtml(string content)
{
var lines = content.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var htmlBuilder = new StringBuilder();
htmlBuilder.Append("<div class='document-content'>");
foreach (var line in lines)
{
if (string.IsNullOrWhiteSpace(line))
{
htmlBuilder.Append("<p> </p>");
}
else
{
htmlBuilder.Append($"<p>{HttpUtility.HtmlEncode(line)}</p>");
}
}
htmlBuilder.Append("</div>");
return htmlBuilder.ToString();
}
private string FormatContentAsHtml(string content)
{
var lines = content.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var htmlBuilder = new StringBuilder();
htmlBuilder.Append("<div class='document-content'>");
foreach (var line in lines)
{
if (string.IsNullOrWhiteSpace(line))
{
htmlBuilder.Append("<p> </p>");
}
else
{
htmlBuilder.Append($"<p>{HttpUtility.HtmlEncode(line)}</p>");
}
}
htmlBuilder.Append("</div>");
return htmlBuilder.ToString();
}
Private Function FormatContentAsHtml(ByVal content As String) As String
Dim lines = content.Split( { Environment.NewLine }, StringSplitOptions.None)
Dim htmlBuilder = New StringBuilder()
htmlBuilder.Append("<div class='document-content'>")
For Each line In lines
If String.IsNullOrWhiteSpace(line) Then
htmlBuilder.Append("<p> </p>")
Else
htmlBuilder.Append($"<p>{HttpUtility.HtmlEncode(line)}</p>")
End If
Next line
htmlBuilder.Append("</div>")
Return htmlBuilder.ToString()
End Function
This method ensures the document content is properly formatted as HTML, with each line wrapped in paragraph tags and empty lines preserved.
3.3 Client-Side JavaScript
To handle the file upload and display the processed content, we use JavaScript:
uploadBtn.addEventListener('click', () => {
const file = fileInput.files[0];
if (!file) {
alert('Please select a file first.');
return;
}
const formData = new FormData();
formData.append('file', file);
uploadBtn.disabled = true;
uploadBtn.textContent = 'Processing...';
documentContent.innerHTML = 'Processing document...';
fetch('/Home/UploadAndProcess', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
documentContent.innerHTML = data.content;
} else {
documentContent.innerHTML = `<p>Error: ${data.message}</p>`;
}
})
.catch(error => {
console.error('Error:', error);
documentContent.innerHTML = '<p>An error occurred while processing the document.</p>';
})
.finally(() => {
uploadBtn.disabled = false;
uploadBtn.textContent = 'Upload and Process';
});
});
uploadBtn.addEventListener('click', () => {
const file = fileInput.files[0];
if (!file) {
alert('Please select a file first.');
return;
}
const formData = new FormData();
formData.append('file', file);
uploadBtn.disabled = true;
uploadBtn.textContent = 'Processing...';
documentContent.innerHTML = 'Processing document...';
fetch('/Home/UploadAndProcess', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
documentContent.innerHTML = data.content;
} else {
documentContent.innerHTML = `<p>Error: ${data.message}</p>`;
}
})
.catch(error => {
console.error('Error:', error);
documentContent.innerHTML = '<p>An error occurred while processing the document.</p>';
})
.finally(() => {
uploadBtn.disabled = false;
uploadBtn.textContent = 'Upload and Process';
});
});
This JavaScript code handles the file upload process, sends the file to the server for processing, and updates the UI with the processed content or error messages.
3.4 Styling the User Interface
The application uses custom CSS to create an attractive and user-friendly interface.
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f7f7f7;
color: #333;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
padding-top: 0.5rem;
}
h1 {
font-weight: 300;
color: #2c3e50;
text-align: center;
margin-bottom: 1rem;
}
.lead {
text-align: center;
color: #7f8c8d;
margin-bottom: 2rem;
}
.upload-area {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 2rem;
text-align: center;
margin-bottom: 2rem;
transition: all 0.3s ease;
}
.upload-area:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
.file-icon {
width: 64px;
height: 64px;
margin-bottom: 1rem;
color: #3498db;
}
.choose-file {
background-color: #ecf0f1;
color: #2c3e50;
border: none;
padding: 0.5rem 1rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
}
.choose-file:hover {
background-color: #d5dbdb;
}
.file-info {
font-size: 0.9em;
color: #95a5a6;
margin-top: 0.5rem;
}
.upload-button {
background-color: #3498db;
color: white;
border: none;
padding: 0.75rem 1.5rem;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s ease;
margin-top: 1rem;
}
.upload-button:hover {
background-color: #2980b9;
}
.content-wrapper {
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
padding: 1rem;
margin-top: 2rem;
}
.content-area {
max-height: 300px;
overflow-y: auto;
padding: 1rem;
background-color: #f9f9f9;
border-radius: 4px;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 14px;
line-height: 1.6;
}
.content-area::-webkit-scrollbar {
width: 8px;
}
.content-area::-webkit-scrollbar-track {
background: #f1f1f1;
border-radius: 4px;
}
.content-area::-webkit-scrollbar-thumb {
background: #bdc3c7;
border-radius: 4px;
}
.content-area::-webkit-scrollbar-thumb:hover {
background: #95a5a6;
}
.document-content p {
margin: 0 0 10px 0;
}
</style>
This CSS creates a clean, modern look with a light color scheme. The upload area features a white background with subtle shadow effects, while the content area has a scrollable design with a light grey background. Using border-radius and box-shadow properties adds depth and visual interest to the interface elements.
4. Exporting Word Documents
As we continue to enhance our Word Document Processor, let's add the ability to export documents. This feature will allow users to generate a new Word document from our application.
4.1 Updating the User Interface
First, we'll add an "Export" option to our navigation bar. Open the _Layout.cshtml file in the Views/Shared folder and locate the
element. Let's add a new list item for our export function:<li class="nav-item">
<a class="nav-link" id="exportLink" href="#" onclick="exportDocument(); return false;"><i class="fas fa-file-export"></i> Export</a>
</li>
<li class="nav-item">
<a class="nav-link" id="exportLink" href="#" onclick="exportDocument(); return false;"><i class="fas fa-file-export"></i> Export</a>
</li>
We're using Font Awesome for the icon, so ensure we have the CSS link in our
section. This code adds an "Export" link to the navigation bar. It uses Font Awesome for the icon and calls the exportDocument() function when clicked. The href="#" and return false prevent the default link behavior.4.2 Implementing Client-Side Export Logic
Now, let's add the JavaScript function that will handle the export process. At the bottom of our _Layout.cshtml file, just before the closing