In today’s digital age, the ability to extract text from images is becoming increasingly important. Whether you’re dealing with scanned documents, business cards, or any other form of image-based data, being able to search and manipulate this information efficiently can save a lot of time and effort. This tutorial will guide you through building an interactive web application using Aspose.OCR and ASP.NET Core that allows users to upload images, search for specific keywords within the extracted text, and view real-time results with highlighted matches.

Source of truth: The code and flow below are aligned with the gist at the end of this article (controller endpoints, AsposeOcr usage, and JSON response shape).

Complete Example


Prerequisites

  • .NET 8 (or .NET 6+) SDK
  • Visual Studio / VS Code
  • NuGet access for Aspose.OCR
  • (Optional) An Aspose license file if you plan to exceed evaluation limits

Step 1: Create the ASP.NET Core MVC Project

dotnet new mvc -n ImageTextSearchApp -f net8.0
cd ImageTextSearchApp

# Add Aspose.OCR
dotnet add package Aspose.OCR

Why MVC? The gist uses controllers (HomeController), views, and a classic Startup—an easy, well-known pattern for file uploads + server-side OCR.


Step 2: Prepare Static Assets and Uploads Folder

The controller writes uploads under wwwroot/uploads. Create that folder and ensure the app can write to it.

mkdir -p wwwroot/uploads

We also enable static files middleware in Startup (shown later) so the wwwroot tree is served correctly.


Create Controllers/HomeController.cs and implement Index + SearchText as per the gist. This action saves the uploaded image, performs OCR, searches a keyword (case-insensitive), and returns JSON.

using System;
using System.IO;
using System.Threading.Tasks;
using Aspose.Ocr;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace ImageTextSearchApp
{
    public class HomeController : Controller
    {
        private readonly IWebHostEnvironment _environment;

        public HomeController(IWebHostEnvironment environment)
        {
            _environment = environment;
        }

        [HttpGet]
        public IActionResult Index()
        {
            return View();
        }

        [HttpPost]
        [RequestSizeLimit(20_000_000)] // ~20MB max, adjust as needed
        public async Task<IActionResult> SearchText(IFormFile imageFile, string searchKeyword)
        {
            if (imageFile == null || imageFile.Length == 0)
                return BadRequest("Image file is required.");
            if (string.IsNullOrWhiteSpace(searchKeyword))
                return BadRequest("Search keyword is required.");

            // Ensure uploads directory exists
            var uploadsDir = Path.Combine(_environment.WebRootPath, "uploads");
            Directory.CreateDirectory(uploadsDir);

            // Sanitize filename and save
            var safeName = Path.GetFileName(imageFile.FileName);
            var filePath = Path.Combine(uploadsDir, safeName);
            using (var stream = new FileStream(filePath, FileMode.Create))
            {
                await imageFile.CopyToAsync(stream);
            }

            // Perform OCR on the uploaded image
            using (var ocrEngine = new AsposeOcr())
            {
                var extractedText = await ocrEngine.RecognizeImageAsync(filePath);

                // Case-insensitive keyword check
                var found = extractedText?.IndexOf(searchKeyword, StringComparison.OrdinalIgnoreCase) >= 0;
                var searchResult = found ? "Keyword found!" : "Keyword not found.";

                // Return minimal JSON (exact shape matches the gist)
                return Json(new { extractedText, searchResult });
            }
        }
    }
}

Notes

  • We added a small file size limit and directory creation for robustness.
  • For production, validate file types (.png/.jpg/.jpeg/.tif) and consider virus scanning.

Step 4: Wire Up Startup and Program

Create Startup.cs and Program.cs as in the gist to enable MVC, static files, routing, and HTTPS redirection.

Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace ImageTextSearchApp
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();         // serves wwwroot (including /uploads)

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Program.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace ImageTextSearchApp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

Step 5: Build the Upload + Search UI (Razor View)

Create Views/Home/Index.cshtml with a simple upload form and a search box. The JavaScript posts the file + keyword to /Home/SearchText, prints OCR text, and highlights matches in the browser (no server changes needed).

@{
    ViewData["Title"] = "Image Text Search";
}

<h1 class="mb-3">Image Text Search (Aspose.OCR + ASP.NET Core)</h1>

<form id="ocrForm" enctype="multipart/form-data" class="mb-4">
    <div class="form-group mb-2">
        <label for="imageFile">Select image</label>
        <input type="file" id="imageFile" name="imageFile" accept=".png,.jpg,.jpeg,.tif,.tiff,.bmp" class="form-control" required />
    </div>

    <div class="form-group mb-2">
        <label for="searchKeyword">Keyword</label>
        <input type="text" id="searchKeyword" name="searchKeyword" class="form-control" placeholder="Enter a word to find..." required />
    </div>

    <button type="submit" class="btn btn-primary">Upload & Search</button>
</form>

<div id="result" class="mt-3">
    <h3>Result</h3>
    <p id="searchStatus" class="fw-bold"></p>
    <pre id="extractedText" style="white-space: pre-wrap"></pre>
</div>

@section Scripts {
<script>
(function () {
    const form = document.getElementById('ocrForm');
    const statusEl = document.getElementById('searchStatus');
    const textEl = document.getElementById('extractedText');

    function escapeHtml(s) {
        return s.replace(/[&<>"']/g, c => ({
            '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
        }[c]));
    }

    function highlight(text, keyword) {
        if (!keyword) return escapeHtml(text);
        const pattern = new RegExp(`(${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
        return escapeHtml(text).replace(pattern, '<mark>$1</mark>');
    }

    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        statusEl.textContent = 'Processing...';
        textEl.textContent = '';

        const formData = new FormData(form);
        try {
            const res = await fetch('/Home/SearchText', {
                method: 'POST',
                body: formData
            });
            if (!res.ok) {
                const msg = await res.text();
                statusEl.textContent = `Error: ${msg}`;
                return;
            }
            const data = await res.json();
            statusEl.textContent = data.searchResult || '';
            const kw = document.getElementById('searchKeyword').value;
            textEl.innerHTML = highlight(data.extractedText || '', kw);
        } catch (err) {
            statusEl.textContent = 'Unexpected error. See console.';
            console.error(err);
        }
    });
})();
</script>
}

This implements real-time highlighting in the extracted text block. Rendering highlights on the image itself would require OCR bounding boxes and drawing overlays (not covered by the gist).


Step 6: Run the App

dotnet run

Open the URL shown in console (e.g., https://localhost:5001). Upload an image, enter a keyword, and click Upload & Search. You’ll see:

  • Keyword found! or Keyword not found. (server-generated)
  • The extracted text with client-side <mark> highlights

Step 7: Production-Ready Considerations (Optional)

  • File validation: Check MIME types/extensions and consider antivirus scanning.
  • Size limits: Use RequestSizeLimit (shown) and reverse proxy/web.config limits as needed.
  • Cleanup: Periodically delete old files from wwwroot/uploads.
  • Localization: If you need multiple languages, configure OCR language options server-side.
  • Error UX: Replace alerts with toasts; add loading spinners and progress bars.

Troubleshooting

  • Empty OCR result: Try higher-quality input, correct orientation, or different format (PNG/TIFF).
  • CORS: If front-end is separate, enable CORS and use the full API URL.
  • HTTPS: Ensure dev cert trust (dotnet dev-certs https --trust) if browser blocks mixed content.

More in this category