如何用Node.js在浏览器打开PDF、DOCX文件?附Laravel文件预览代码解析
Hey there! Let's tackle your two questions step by step:
Browsers handle PDFs natively, but DOCX needs extra work since there's no built-in support. Here are practical implementations:
For PDF Files
You can directly send the file with the correct Content-Type header, and browsers will render it inline. Using Express as an example:
const express = require('express'); const path = require('path'); const app = express(); // Route to preview PDF app.get('/preview-pdf', (req, res) => { const pdfFilePath = path.join(__dirname, 'sample.pdf'); // Set header to tell browser to preview instead of download res.setHeader('Content-Type', 'application/pdf'); res.sendFile(pdfFilePath); }); app.listen(3000, () => console.log('Server up on port 3000'));
For DOCX Files
Since browsers don't support DOCX preview out of the box, you have two main options:
Option 1: Convert DOCX to HTML (Server-Side)
Use mammoth.js to convert DOCX content to HTML, then send the HTML to the browser:
const express = require('express'); const mammoth = require('mammoth'); const fs = require('fs'); const path = require('path'); const app = express(); // Route to preview DOCX as HTML app.get('/preview-docx', async (req, res) => { const docxFilePath = path.join(__dirname, 'sample.docx'); try { const conversionResult = await mammoth.convertToHtml({ path: docxFilePath }); // Wrap converted HTML in a basic page for better formatting const previewHtml = ` <!DOCTYPE html> <html> <head> <title>DOCX Preview</title> <style>body { padding: 2rem; font-family: Arial; }</style> </head> <body>${conversionResult.value}</body> </html> `; res.setHeader('Content-Type', 'text/html'); res.send(previewHtml); } catch (error) { console.error('DOCX conversion failed:', error); res.status(500).send('Could not preview DOCX file'); } }); app.listen(3000, () => console.log('Server running on port 3000'));
Option 2: Use Frontend Library to Render DOCX
Serve the DOCX file as a blob, then use a frontend library like docx-preview to render it:
Backend (Express)
app.get('/get-docx', (req, res) => { const docxFilePath = path.join(__dirname, 'sample.docx'); res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); res.sendFile(docxFilePath); });
Frontend (HTML)
<!DOCTYPE html> <html> <head> <title>DOCX Preview</title> <script src="https://unpkg.com/docx-preview@0.1.17/dist/docx-preview.js"></script> </head> <body> <div id="docx-container" style="padding: 2rem;"></div> <script> // Fetch the DOCX file and render it fetch('/get-docx') .then(res => res.blob()) .then(blob => { docx.renderAsync(blob, document.getElementById('docx-container')); }); </script> </body> </html>
This snippet is a Laravel controller method that handles file preview based on MIME type, plus logs file access. Let's go line by line:
$doc = Document::findOrFail($id);
- Uses Laravel's Eloquent ORM to fetch a
Documentrecord by ID. If no record exists, it automatically throws a 404 error (that's whatfindOrFail()does).
$path = Storage::disk('local')->getDriver()->getAdapter()->applyPathPrefix($doc->file);
- Gets the full physical path of the file on the server.
$doc->fileis the relative path stored in the database;applyPathPrefix()adds the storage disk's base path (e.g.,storage/app/for the local disk) to get the absolute path.
$type = $doc->mimetype;
- Pulls the file's MIME type (like
application/pdforapplication/vnd.openxmlformats-officedocument.wordprocessingml.document) from the database record.
Log::addToLog('Document ID '.$id.' à été consulté');
- Logs that the document was accessed. The message is in French ("Document ID [X] has been viewed").
addToLog()is likely a custom logging method added to the project (Laravel's default usesLog::info()/Log::error()etc.).
if ($type == 'application/pdf' || $type == 'image/jpeg' || $type == 'image/png' || $type == 'image/jpg' || $type == 'image/gif') { return response()->file($path, ['Content-Type' => $type]); } elseif ($type == 'video/mp4' || $type == 'audio/mpeg' || $type == 'audio/mp3' || $type == 'audio/x-m4a') { return view('documents.play',compact('doc')); } else { return response()->file($path, ['Content-Type' => $type]); }
- First condition: For PDFs and common image types, it returns the file directly with the correct
Content-Typeheader. Browsers will render these files inline (no download prompt). - Elseif condition: For audio/video files, it returns a dedicated view (
documents.play) and passes the$docdata to it. This view probably includes an embedded player for media files. - Else block: For all other file types (like DOCX), it also returns the file directly. Note: Browsers can't preview these types natively, so this will usually trigger a download instead of inline preview.
A small optimization note: The response()->file() call is duplicated in the first and else blocks—you could refactor this to avoid repetition.
内容的提问来源于stack exchange,提问作者amine SGHIR




