Build a Document Detective with Tika
Detect file types by content and extract metadata using Apache Tika CLI.
Introduction
30 Second Summary
Every file on your computer has a true identity hidden in its first few bytes. Rename a PDF to .txt and your operating system gets fooled, but the right tool can read those hidden bytes and reveal what the file actually is.
In this project, you will build a "Document Detective" using Apache Tika's command-line interface. You'll analyze mystery files with misleading extensions, extract their text and metadata, and wrap everything into a reusable PowerShell script that produces a structured report.
What You'll Build
Picture dropping a folder of mystery files into your terminal and watching a script instantly identify every file's true format, pull out its text and metadata, and produce a clean JSON report you can hand to any downstream tool.
By the end of this project, you'll have:
- A working content-based detection workflow that correctly identifies files regardless of their extension, so you can spot mislabeled or disguised documents.
- A text and metadata extraction pipeline that pulls readable content, author info, page counts, and creation dates from PDFs, Office docs, and archives.
- A reusable batch analysis script that processes any folder you point it at and produces a complete JSON report with one command.
- Secret Mission: Add language detection and SHA-256 content hashing to your report for forensic-grade analysis.
Are there any prerequisites?
You need Java 11 or higher installed on your machine. This project uses Java to run the Tika jar file from the command line.
Basic familiarity with typing commands in a terminal is helpful, but every command is provided for you to copy.
Before We Start
Before We Start...
Before we dive in, let's lock in what you're about to build and why it matters.
Download Tika and Verify It Works
Your Document Detective needs a brain. Apache Tika is a content analysis toolkit that can identify and extract text from over a thousand file formats.
Tika ships as a single jar file. No package manager, no classpath configuration, no extra dependencies beyond Java. You download one file and you're ready to go.
In this step, get ready to:
- Download tika-app-3.3.1.jar from the Apache mirror.
- Confirm Java can execute the jar.
- Explore the file types Tika supports.
Download tika-app-3.3.1.jar
The tika-app jar bundles every parser and detector Tika offers into a single 62 MB file. You will download it from the official Apache CDN and place it inside a dedicated project folder.
- In VSCodium, click Terminal in the top menu bar.
- Click New Terminal.
What is this terminal?
The integrated terminal lets you run PowerShell commands without leaving your editor. Every command in this project runs here.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your project folder and move into it by running these commands:
mkdir document-detective
cd document-detective
- Download the Tika jar from the Apache mirror by running this command:
Invoke-WebRequest -Uri "https://dlcdn.apache.org/tika/3.3.1/tika-app-3.3.1.jar" -OutFile "tika-app-3.3.1.jar"
What does this command do?
Invoke-WebRequest is PowerShell's built-in command for downloading files from the internet. The -Uri flag points to the Apache download server, and -OutFile saves it with the name you specify.
The file is 62 MB, so the download may take a minute depending on your connection. You will see a progress indicator in the terminal while it runs.
- Confirm the file downloaded by running:
ls
- You should see tika-app-3.3.1.jar listed in the output.
Don't see the jar file?
- Check your internet connection and try the Invoke-WebRequest command again.
- If you get a 404 or connection error, visit https://dlcdn.apache.org/tika/3.3.1/ in your browser to confirm the file is available, then download it manually and move it into your document-detective folder.
- Make sure you are inside the document-detective folder. Run pwd to check your current location.
help me download tika-app-3.3.1.jar in PowerShell
Verify Tika runs
The jar is on disk, but you need to confirm Java can actually execute it. Tika's --version flag is the quickest way to check.
- Before you run this, what do you expect to see if both Java and Tika are working correctly?
- Run Tika's version check by running this command:
java -jar tika-app-3.3.1.jar --version
You should see Apache Tika 3.3.1 printed to the terminal.
✔️ I see Apache Tika 3.3.1
Tika is ready. Java found the jar and executed it successfully. You are all set to start analyzing files.
ⓧ I see an error about Java or the jar
- If you see a message about Java not being recognized, your Java installation is not on the system PATH. Reinstall Java and make sure to check the option to add it to PATH during installation.
- If you see a message about being unable to access the jarfile, confirm you are inside the document-detective folder by running pwd. Then run ls to check the jar is there.
- Verify your Java version by running:
java --version
You need Java 11 or higher. Any version from 11 upwards works with Tika 3.3.1.
Explore what Tika can detect
Tika recognizes over a thousand file formats out of the box. You can see the full list with a single flag.
- See all the file types Tika supports by running this command:
java -jar tika-app-3.3.1.jar --list-supported-types
A long list of MIME types scrolls past. You will see entries like application/pdf, image/png, and application/vnd.openxmlformats-officedocument.wordprocessingml.document (that last one is a DOCX file).
What is a MIME type?
A MIME type is a standardized label that identifies a file's format by its content structure. For example, application/pdf means the file is a PDF regardless of what extension it has.
Tika uses these labels internally when it detects a file. In the next step, you will see Tika return these MIME types when you feed it mystery files.
Scroll through the output
The list is very long. You do not need to read every line. The point is to see how many formats Tika covers with zero extra configuration.
Tika is downloaded, verified, and ready to analyze files. Next up, you will feed it documents with misleading extensions and watch it correctly identify what they really are.
Detect File Types by Content
In the previous step you downloaded Tika and confirmed it runs. Now it is time for Tika to earn its keep.
File extensions lie. A file named .txt might actually be a PDF, and a .dat file might be a Word document. Tika solves this by reading the actual bytes of the file to determine its real format. In this step, you will create files with misleading extensions and watch Tika correctly identify them.
In this step, get ready to:
- Create sample files with misleading or swapped extensions.
- Run Tika's detect mode on each file to reveal its true type.
- Confirm that Tika ignores extensions and reads actual content.
Create sample files with misleading extensions
You need test files that will fool anyone looking at the file extension. You will create three files: a real PDF disguised as a .txt file, a real DOCX disguised as a .dat file, and a plain text file wearing a .pdf extension.
- Create a test-files folder and your first two mystery files by running these commands:
# Create the test-files directory
mkdir test-files
# Create a plain text file disguised with a .pdf extension
Set-Content -Path .\test-files\fake.pdf -Value "This is just a plain text file pretending to be a PDF."
# Create a minimal valid PDF disguised with a .txt extension
$pdfContent = "%PDF-1.0`n1 0 obj<</Type/Catalog/Pages 2 0 R>>endobj 2 0 obj<</Type/Pages/Kids[3 0 R]/Count 1>>endobj 3 0 obj<</Type/Page/MediaBox[0 0 3 3]>>endobj`n%%EOF"
[System.IO.File]::WriteAllText("$PWD\test-files\mystery-file.txt", $pdfContent)
What does this code do?
The first file (fake.pdf) is genuinely plain text. Its .pdf extension is a lie.
The second file (mystery-file.txt) contains actual PDF content starting with the %PDF-1.0 header. These opening bytes are called magic bytes. They tell any byte-level reader the true format, regardless of the extension.
- Confirm both files were created by running:
ls .\test-files
You should see fake.pdf and mystery-file.txt listed.
Now you need a real DOCX file. A DOCX is actually a ZIP archive containing XML files. You will build one from scratch using PowerShell's .NET compression library.
- Create a valid DOCX file disguised with a .dat extension by running this command:
# Load .NET compression to build a valid DOCX (a ZIP with XML inside)
Add-Type -AssemblyName System.IO.Compression.FileSystem
$stream = [System.IO.File]::Create("$PWD\test-files\mystery-file.dat")
$zip = New-Object System.IO.Compression.ZipArchive($stream, [System.IO.Compression.ZipArchiveMode]::Create)
# Add the content types definition (required for OOXML)
$entry = $zip.CreateEntry("[Content_Types].xml")
$w = New-Object System.IO.StreamWriter($entry.Open())
$w.Write('<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/></Types>')
$w.Close()
# Add the relationships file
$entry = $zip.CreateEntry("_rels/.rels")
$w = New-Object System.IO.StreamWriter($entry.Open())
$w.Write('<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>')
$w.Close()
# Add the actual document content
$entry = $zip.CreateEntry("word/document.xml")
$w = New-Object System.IO.StreamWriter($entry.Open())
$w.Write('<?xml version="1.0" encoding="UTF-8"?><w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body><w:p><w:r><w:t>Hello from Document Detective</w:t></w:r></w:p></w:body></w:document>')
$w.Close()
# Finalize and close the archive
$zip.Dispose()
$stream.Close()
What does this code do?
A DOCX file is secretly a ZIP archive with a specific folder structure inside. Tika's container-aware detection opens the ZIP and inspects the internal XML to determine it is a Word document.
The [Content_Types].xml file tells Tika which MIME type each internal part uses. That is how Tika distinguishes a DOCX from an ordinary ZIP.
- Confirm all three test files exist by running:
ls .\test-files
You should now see fake.pdf, mystery-file.dat, and mystery-file.txt listed.
Not seeing all three files?
- If mystery-file.dat is missing, make sure you ran the entire DOCX creation block in one paste. Copying only part of it leaves the ZIP stream open.
- If you see an error about the path, confirm you are still in the document-detective folder by running pwd.
- Help me troubleshoot my missing test files.
Run Tika's detect mode on each file
Tika's -d flag (short for --detect) reads the actual content of a file and prints its true MIME type. It completely ignores the file extension.
Before you run the first command, think about this: mystery-file.txt has a .txt extension. Do you think Tika will report text/plain or something else?
- Run Tika's detect mode on the disguised PDF by running this command:
java -jar tika-app-3.3.1.jar -d .\test-files\mystery-file.txt
You should see application/pdf printed to the terminal. Tika found the %PDF- magic bytes at the start of the file and correctly identified it as a PDF, despite the .txt extension.
- Now detect the disguised DOCX by running:
java -jar tika-app-3.3.1.jar -d .\test-files\mystery-file.dat
You should see application/vnd.openxmlformats-officedocument.wordprocessingml.document printed. That long MIME type is the official identifier for a Word DOCX file.
- Finally, detect the fake PDF by running:
java -jar tika-app-3.3.1.jar -d .\test-files\fake.pdf
You should see text/plain printed. Even though the file is named fake.pdf, Tika reads the content and knows it is just plain text.
Seeing a different MIME type than expected?
- If mystery-file.txt shows text/plain instead of application/pdf, the PDF content was not written correctly. Delete the file and re-run the [System.IO.File]::WriteAllText command, making sure the string starts with %PDF-1.0.
- If mystery-file.dat shows application/zip instead of the DOCX type, the internal XML structure was not created properly. Delete the file and paste the DOCX creation block again.
- Help me fix my Tika detection results.
Compare detection results against the actual file types
Here is what you just proved. Tika identified every file by its actual content, not its extension.
- mystery-file.txt (actually a PDF): detected as application/pdf.
- mystery-file.dat (actually a DOCX): detected as application/vnd.openxmlformats-officedocument.wordprocessingml.document.
- fake.pdf (actually plain text): detected as text/plain.
Why does this matter?
In real-world systems, files arrive via email attachments, user uploads, and API responses. Their extensions can be wrong, missing, or intentionally misleading. Content-based detection gives you a reliable answer regardless of how the file is named.
Your Document Detective can now see through file extension disguises. Next up, you will go deeper and extract the actual text and metadata hidden inside these files.
Extract Text and Metadata
Detection tells you what a file is. But in most real workflows, you need what's actually inside: the text content for searching and indexing, and the metadata for cataloging and organizing.
In this step, you'll use Tika's extraction modes to pull readable text and structured metadata from your documents. Then you'll hit a wall when you try the same approach on a ZIP archive.
In this step, get ready to:
- Extract readable text from PDF and DOCX files using the -t flag.
- Pull structured metadata as JSON using the -j flag.
- Discover the limitation of simple extraction on archive files.
Extract plain text from documents
You proved in the last step that mystery-file.txt is actually a PDF and mystery-file.dat is actually a DOCX. Now you'll copy them with their correct extensions so Tika can extract their content cleanly.
- Create properly-named copies of your test files by running these commands:
Copy-Item .\test-files\mystery-file.txt .\test-files\sample.pdf
Copy-Item .\test-files\mystery-file.dat .\test-files\sample.docx
- Confirm both files exist by running:
ls .\test-files\sample.*
You should see sample.pdf and sample.docx listed.
Tika's -t flag strips away all formatting and returns just the readable text content of a file. This is what you'd feed into a search engine or text analysis pipeline.
- Extract the plain text from your PDF by running:
java -jar tika-app-3.3.1.jar -t .\test-files\sample.pdf
You should see the readable text content of your PDF printed to the terminal, with no formatting or layout information.
- Now extract text from the DOCX file by running:
java -jar tika-app-3.3.1.jar -t .\test-files\sample.docx
Again, you see clean plain text. Tika handles the internal XML structure of DOCX files and gives you just the words.
What does the -t flag actually do?
The --text flag (-t for short) tells Tika to parse the file using the appropriate parser and output only the extracted text content. It discards all structural markup, styling, and embedded objects.
This makes it ideal for feeding document content into search indexes, NLP pipelines, or anywhere you need raw text without formatting noise.
Seeing garbled output or an error?
If you see an error about the file not being found, double-check the path uses .\test-files\ and that your terminal is in the document-detective folder.
If you see garbled characters, your file may not have copied correctly. Re-run the Copy-Item commands and try again.
help me troubleshoot Tika text extraction errors
Extract metadata as JSON
Text content is only half the picture. Documents carry rich metadata: who created them, when, how many pages, what software was used. Tika's -j flag extracts all of this as structured JSON you can pipe into other tools.
- Extract metadata from your PDF as JSON by running:
java -jar tika-app-3.3.1.jar -j .\test-files\sample.pdf
You should see a JSON object printed to the terminal containing keys like Content-Type, dc:creator, and xmpTPg:NPages.
What do these metadata keys mean?
- Content-Type is the detected MIME type of the file (same as what -d returns).
- dc:creator is the document author, using the Dublin Core metadata standard.
- xmpTPg:NPages is the page count, from Adobe's XMP metadata schema.
- Tika pulls metadata from multiple embedded standards and normalizes them into a single JSON object.
- Now extract metadata from your DOCX file by running:
java -jar tika-app-3.3.1.jar -j .\test-files\sample.docx
Notice how the DOCX metadata includes different keys than the PDF. Each file format stores metadata in its own way, but Tika presents it all consistently as JSON.
Try extracting from a ZIP archive and observe the limitation
So far you've extracted text and metadata from individual files. But what happens when documents are bundled together inside an archive? Let's find out.
- Create a ZIP archive containing both of your sample documents by running:
Compress-Archive -Path .\test-files\sample.pdf, .\test-files\sample.docx -DestinationPath .\test-files\sample.zip
- Confirm the ZIP was created by running:
ls .\test-files\sample.zip
You should see sample.zip listed. This archive contains two documents inside it.
Before you run the next command, think about this: you've been using -t to extract text from individual files. What do you think will happen when you point it at a ZIP containing multiple documents?
- Try extracting text from the ZIP by running:
java -jar tika-app-3.3.1.jar -t .\test-files\sample.zip
You'll see either empty output or a jumbled partial result. The text from the individual documents inside the ZIP is missing or incomplete.
Why does this happen?
Tika's simple -t flag does not recurse into embedded documents by default. It treats the ZIP as a single container and only extracts top-level metadata, not the content of the files packed inside.
This is a real limitation. Any time you have archives, email attachments, or compound documents (like a DOCX with embedded images), you need a different approach.
Seeing a full wall of text instead of empty output?
Depending on your Tika version and the archive structure, you might see concatenated text fragments rather than nothing. Either way, the output is not structured per file. You cannot tell which text came from which embedded document. That is the core limitation.
help me understand why Tika's text extraction on my ZIP isn't working as expected
This gap is exactly what you'll solve in the next step. Tika has a recursive extraction mode that digs into every embedded file individually and gives you structured results for each one.
Handle Embedded and Nested Documents
In the previous step, you saw that running --text on your ZIP archive produced partial or empty results. That gap exposed a real limitation: Tika's basic extraction flags do not recurse into embedded documents by default.
Archives, email attachments, and compound Office documents all contain files inside files. To analyze them properly, you need Tika's recursive mode, which dives into every nested layer and extracts metadata and content from each embedded file individually.
In this step, get ready to:
- Use --jsonRecursive to extract metadata and content from every file inside an archive.
- Extract embedded files physically into a folder on disk.
- Combine recursive mode with plain text output for readable results.
Use --jsonRecursive to extract from the ZIP
The --jsonRecursive flag (short form -J) tells Tika to parse the container and every embedded file inside it. It returns a JSON array where each element is a separate metadata object for one embedded document.
- Run the recursive JSON extraction on your ZIP file by running this command:
java -jar tika-app-3.3.1.jar -J .\test-files\sample.zip
Before you run this, what do you expect to see differently compared to the --text output you tried earlier?
You should see a JSON array printed to the terminal. Each element has its own Content-Type and X-TIKA:content field, representing one file from inside the archive.
What does this output mean?
The first JSON object in the array represents the container (the ZIP file itself). Every subsequent object is an embedded file that Tika found inside.
Each object carries its own Content-Type so you know the true format of each embedded file. The X-TIKA:content field holds the extracted text for that specific file.
Seeing an empty array or a single object?
Make sure you used uppercase -J (recursive JSON), not lowercase -j (single-file JSON). Lowercase -j only returns metadata for the container itself.
Also confirm that sample.zip actually contains files. If the archive is empty, Tika has nothing to recurse into.
help me troubleshoot why -J is not showing embedded documents
Extract attachments to a directory
Sometimes you want the actual embedded files on disk, not just their metadata in the terminal. Tika's --extract flag (-z) pulls embedded files out of the container and writes them to a directory you specify.
- Extract the files from your ZIP archive into a new extracted folder by running this command:
java -jar tika-app-3.3.1.jar -z --extract-dir=extracted .\test-files\sample.zip
What does this command do?
- -z tells Tika to extract all embedded attachments from the container file.
- --extract-dir=extracted specifies the target directory where Tika writes the extracted files. Tika creates this folder if it does not exist.
- Confirm the extracted files exist by running:
ls extracted
You should see the individual files that were inside sample.zip now listed in the extracted folder.
Don't see the extracted folder?
Check that you included the equals sign in --extract-dir=extracted with no space between the flag and the directory name.
Also confirm your terminal is in the document-detective project folder, not inside test-files.
help me figure out why --extract-dir is not creating my folder
Combine with --text for recursive plain text
The -J flag outputs XHTML content by default. If you want clean, readable plain text from every embedded file instead, combine -J with -t.
- Run the recursive extraction with plain text output by running this command:
java -jar tika-app-3.3.1.jar -J -t .\test-files\sample.zip
You should see the same JSON array structure, but now the X-TIKA:content field in each object contains plain text instead of XHTML markup.
When would you use each approach?
Use -J alone when you need the full document structure (headings, tables, formatting) preserved as XHTML. Use -J -t when you need clean text for search indexing or preview snippets.
Use -z with --extract-dir when you need the actual embedded files saved to disk for further processing.
Before you run the final check, what two fields do you expect to find in each JSON array element that prove Tika successfully recursed into the archive?
- Verify recursive extraction works end to end by running:
java -jar tika-app-3.3.1.jar -J .\test-files\sample.zip
You should see a JSON array where each element has its own Content-Type and X-TIKA:content field for each file inside the archive. That confirms the gap from the previous step is fully solved.
You now have three ways to handle embedded documents: recursive JSON for metadata, physical extraction for files on disk, and recursive plain text for readable content. Next up, you will combine these capabilities into a reusable script that analyzes an entire folder automatically.
Build a Batch Analysis Script
You can now detect, extract, and recurse into any file Tika supports. The next challenge is scale.
Running commands one file at a time works for a handful of documents, but a real project folder might hold dozens or hundreds of files. In this step you will use Tika's built-in batch mode, then build a reusable PowerShell script that produces a single JSON report for any folder you point it at.
In this step, get ready to:
- Run Tika's batch mode to process an entire directory at once.
- Write a PowerShell script that combines detection, metadata, and text extraction into one JSON report.
- Execute the script against your test folder and verify the output.
Use Tika's built-in batch mode
Tika ships with a batch mode that accepts an input directory and an output directory. It processes every file in the input and writes one result file per document into the output.
- Create an output folder for the batch results by running:
mkdir batch-output
- Run Tika in batch mode with plain text output by running:
java -jar tika-app-3.3.1.jar .\.\test-files .\batch-output -t
- List the generated files by running:
ls .\batch-output
You should see one output file for each input file. Each contains the extracted plain text.
What does batch mode give you?
Batch mode is great for dumping text or XHTML from many files at once. However, it writes separate output files and does not combine detection, metadata, and text into a single structured report.
To get a tailored JSON report with exactly the fields you need, you will build a custom PowerShell script that calls Tika's individual flags per file and merges the results.
Create the PowerShell script
Your script will accept a target folder path, loop through every file, and call Tika three times per file: once for detection, once for metadata, and once for text. It combines the results into a single JSON array.
- Create a new file called document-detective.ps1 in your document-detective folder. In VSCodium, click File then New File.
- Save the empty file as document-detective.ps1 in the project root (next to tika-app-3.3.1.jar).
- Add the parameter block and input validation by pasting this code:
param(
[Parameter(Mandatory=$true)]
[string]$TargetFolder,
[string]$TikaJar = ".\tika-app-3.3.1.jar",
[string]$OutputFile = ".\report.json"
)
# Verify the target folder exists
if (-not (Test-Path $TargetFolder)) {
Write-Error "Target folder not found: $TargetFolder"
exit 1
}
# Verify the Tika jar exists
if (-not (Test-Path $TikaJar)) {
Write-Error "Tika jar not found: $TikaJar"
exit 1
}
# Get all files in the target folder (non-recursive)
$files = Get-ChildItem -Path $TargetFolder -File
$report = @()
What does this code do?
- param(...) defines three parameters: $TargetFolder (mandatory), $TikaJar (defaults to the jar in the current folder), and $OutputFile (defaults to report.json).
- The two Test-Path checks prevent confusing errors later by stopping early if inputs are missing.
- Get-ChildItem -File lists only files (not subdirectories) in the target folder.
- Save document-detective.ps1.
- Verify the parameter block works by running the script with no arguments:
.\document-detective.ps1
PowerShell should prompt you for TargetFolder since it is mandatory. Press Ctrl+C to cancel.
Seeing a script execution policy error?
PowerShell may block script execution by default. Run this command to allow scripts in the current session:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Then try running .\document-detective.ps1 again.
Help me fix my PowerShell execution policy error.
- Now add the main processing loop below the existing code in document-detective.ps1:
foreach ($file in $files) {
Write-Host "Analyzing: $($file.Name)"
# Detect MIME type
$detectedType = java -jar $TikaJar -d $file.FullName 2>$null
$detectedType = $detectedType.Trim()
# Extract metadata as JSON
$metadataRaw = java -jar $TikaJar -j $file.FullName 2>$null
$metadata = $metadataRaw | ConvertFrom-Json
# Extract plain text (first 500 characters as preview)
$textContent = java -jar $TikaJar -t $file.FullName 2>$null
$textPreview = if ($textContent.Length -gt 500) {
$textContent.Substring(0, 500) + "..."
} else {
$textContent
}
# Build report entry
$entry = [PSCustomObject]@{
fileName = $file.Name
fileSizeKB = [math]::Round($file.Length / 1024, 2)
detectedType = $detectedType
metadata = $metadata
textPreview = $textPreview.Trim()
}
$report += $entry
}
What does this loop do?
For each file in the target folder, the script makes three Tika calls:
- -d detects the true MIME type (the same content-based detection you used earlier).
- -j extracts structured metadata as JSON, then ConvertFrom-Json parses it into a PowerShell object.
- -t extracts plain text. The script takes only the first 500 characters as a preview to keep the report readable.
The 2>$null suppresses Tika's warning messages so they do not pollute the output.
- Save document-detective.ps1.
- Test the loop by running the script against your test folder:
.\document-detective.ps1 .\test-files
You should see an "Analyzing: ..." line printed for each file in test-files. The script finishes without writing a report file yet because you have not added the output section.
- Add the report output section below the loop in document-detective.ps1:
# Write report to JSON file
$report | ConvertTo-Json -Depth 5 | Out-File -FilePath $OutputFile -Encoding UTF8
Write-Host ""
Write-Host "Report generated: $OutputFile"
Write-Host "Files analyzed: $($report.Count)"
What does this section do?
ConvertTo-Json -Depth 5 serializes the entire report array to JSON, going 5 levels deep so nested metadata objects are fully rendered.
Out-File writes the JSON string to report.json in UTF-8 encoding. The final Write-Host lines confirm where the report was saved and how many files were processed.
- Save document-detective.ps1.
✔️ Awesome, I've got everything!
Great. Double check you have saved document-detective.ps1 and it contains all three sections: parameters, the foreach loop, and the output block.
ⓧ I'd like to double check the full code
Your document-detective.ps1 should contain these three sections in order. Scroll up and confirm each is present:
- The param(...) block with $TargetFolder, $TikaJar, and $OutputFile parameters, followed by two Test-Path validation checks, Get-ChildItem, and $report = @().
- The foreach ($file in $files) loop containing -d detection, -j metadata extraction, -t text extraction with the 500-character preview, and the [PSCustomObject] entry builder.
- The output section with ConvertTo-Json -Depth 5 piped to Out-File, plus the two Write-Host confirmation lines.
Run the script and verify the report
Before you run the final version, think about what you expect. The script should analyze every file in test-files and produce a JSON report.
- How many files are in your test-files folder? What file count do you expect the script to report?
- Run the completed script by running:
.\document-detective.ps1 .\test-files
You should see "Analyzing: ..." printed for each file, followed by "Report generated: .\report.json" and a file count matching the number of documents in your test-files folder.
- Confirm the report file was created by running:
ls .\report.json
- Inspect the report content by running:
Get-Content .\report.json | Select-Object -First 30
You should see a JSON array where each object has fileName, fileSizeKB, detectedType, metadata, and textPreview fields.
Report file is empty or missing?
- Make sure you saved all three sections in document-detective.ps1 before running it. The output section must come after the closing } of the foreach loop.
- Check that tika-app-3.3.1.jar is in the same folder as the script. The default $TikaJar path is relative to where you run the script from.
- If you see a ConvertFrom-Json error, one of your test files may be producing unexpected output. Try removing it from the folder and re-running.
Help me debug why my document-detective script is not generating a report.
Your Document Detective is complete. You now have a reusable script that produces a structured JSON report for any folder of documents. Next up, put it to the test with the Secret Mission.
Secret mission
Add Language Detection and Content Hashing
Your document detective can identify file types and extract content. But a forensic-grade tool needs two more capabilities: knowing what language each document is written in, and having a unique fingerprint for every file to detect duplicates or verify integrity.
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- tika-app-3.3.1.jar (62 MB download in your project folder).
- document-detective.ps1 (your batch analysis script).
- test-files/ directory (sample documents with misleading extensions).
- extracted/ directory (files pulled from the ZIP archive).
- report.json (generated analysis output).
Keep everything running
No action needed. Choose this if you plan to reuse the Tika jar or your Document Detective script in future projects.
- Your project folder stays on your Desktop with the jar, script, test files, and report intact.
- You can run .\document-detective.ps1 against any new folder at any time.
- Nothing was installed system-wide. Java was already on your machine before this project.
Pause - I'll come back to this later
There are no running processes or services to shut down. Everything in this project is file-based.
- Your project folder remains on your Desktop. No background processes are consuming memory.
- When you come back, open a terminal in your project folder and pick up where you left off.
Delete - I don't want to use this again
Remove your entire project folder. Nothing was installed system-wide, so deleting the folder is a complete cleanup.
- Close any terminals or VSCodium windows that have the project folder open.
- Navigate to your Desktop and delete the project folder.
On macOS or Linux, run:
cd ~/Desktop
rm -rf document-detective
On Windows (PowerShell), run:
cd ~\Desktop
Remove-Item -Recurse -Force document-detective
- Confirm the folder is gone by running ls (macOS/Linux) or Get-ChildItem (Windows) on your Desktop. You should no longer see the project folder listed.
That's a Wrap!
That's a Wrap!
Nice work! You just built a Document Detective that analyzes files by their actual content, not their names.
You went from running a single detection command to owning a reusable script that produces a full analysis report for any folder you throw at it.
Here's what you learned:
- Used content-based MIME type detection to correctly identify files regardless of misleading or missing extensions.
- Solved the embedded document problem using --jsonRecursive to extract metadata and content from every file inside an archive.
- Built a reusable PowerShell batch analysis script that produces a structured JSON report with detection, metadata, and text previews for any folder.
- Secret Mission: Added language detection and SHA-256 content hashing for forensic-grade document analysis.
Ready to quiz yourself?