桐乡九九信息网

使用JavaScript将JPG格式图片转为BMP格式的完整代码

发布:2026-03-24 15:34 浏览:0
特别声明:本页信息由用户及第三方发布,真实性、合法性由发布人负责。详情请阅读九九信息网免责条款
详细信息

方案1:纯前端实现(Canvas API)

<!DOCTYPE html>
<html>
<head>
    <title>JPG to BMP Converter</title>
</head>
<body>
    <input type="file" id="fileInput" accept="image/jpeg,image/jpg">
    <button onclick="convertToBMP()">转换为BMP</button>
    <canvas id="canvas" style="display:none;"></canvas>

    <script>
        // BMP文件头结构
        class BMPEncoder {
            constructor(width, height, data) {
                this.width = width;
                this.height = height;
                this.data = data;
            }

            encode() {
                const width = this.width;
                const height = this.height;
                const data = this.data;

                // 每行字节数必须是4的倍数
                const rowSize = Math.floor((width * 3 + 3) / 4) * 4;
                const fileSize = 54 + rowSize * height;

                // 创建ArrayBuffer
                const buffer = new ArrayBuffer(fileSize);
                const view = new DataView(buffer);

                // BMP文件头 (14字节)
                // 文件类型标识
                view.setUint8(0, 0x42); // 'B'
                view.setUint8(1, 0x4D); // 'M'

                // 文件大小
                view.setUint32(2, fileSize, true);

                // 保留字段
                view.setUint16(6, 0, true);
                view.setUint16(8, 0, true);

                // 像素数据偏移量
                view.setUint32(10, 54, true);

                // DIB头 (BITMAPINFOHEADER - 40字节)
                view.setUint32(14, 40, true); // DIB头大小
                view.setInt32(18, width, true); // 宽度
                view.setInt32(22, height, true); // 高度
                view.setUint16(26, 1, true); // 颜色平面数
                view.setUint16(28, 24, true); // 每像素位数 (24位RGB)
                view.setUint32(30, 0, true); // 压缩方式 (BI_RGB)
                view.setUint32(34, 0, true); // 图像大小 (0表示不压缩)
                view.setInt32(38, 0, true); // 水平分辨率
                view.setInt32(42, 0, true); // 垂直分辨率
                view.setUint32(46, 0, true); // 调色板颜色数
                view.setUint32(50, 0, true); // 重要颜色数

                // 像素数据 (BGR格式,非RGB)
                let offset = 54;

                // BMP存储是从下到上的,所以需要反向写入行
                for (let y = height - 1; y >= 0; y--) {
                    for (let x = 0; x < width; x++) {
                        const pixelIndex = (y * width + x) * 4;

                        // 获取RGB值 (Canvas返回的是RGBA)
                        const r = data[pixelIndex];
                        const g = data[pixelIndex + 1];
                        const b = data[pixelIndex + 2];

                        // BMP使用BGR顺序
                        view.setUint8(offset++, b);
                        view.setUint8(offset++, g);
                        view.setUint8(offset++, r);
                    }

                    // 填充到4字节对齐
                    const padding = rowSize - width * 3;
                    for (let p = 0; p < padding; p++) {
                        view.setUint8(offset++, 0);
                    }
                }

                return buffer;
            }
        }

        async function convertToBMP() {
            const fileInput = document.getElementById('fileInput');
            const canvas = document.getElementById('canvas');
            const ctx = canvas.getContext('2d');

            if (!fileInput.files[0]) {
                alert('请选择JPG文件');
                return;
            }

            const file = fileInput.files[0];

            // 验证文件类型
            if (!file.type.match('image/jpeg') && !file.name.match(/\.jpe?g$/i)) {
                alert('请选择JPG格式的图片');
                return;
            }

            try {
                // 创建Image对象
                const img = new Image();

                // 创建文件URL
                const imgUrl = URL.createObjectURL(file);

                // 等待图片加载
                await new Promise((resolve, reject) => {
                    img.onload = resolve;
                    img.onerror = reject;
                    img.src = imgUrl;
                });

                // 设置Canvas尺寸
                canvas.width = img.width;
                canvas.height = img.height;

                // 绘制图片到Canvas
                ctx.drawImage(img, 0, 0);

                // 获取图像数据
                const imageData = ctx.getImageData(0, 0, img.width, img.height);

                // 创建BMP编码器
                const encoder = new BMPEncoder(img.width, img.height, imageData.data);

                // 编码为BMP
                const bmpBuffer = encoder.encode();

                // 创建Blob并下载
                const blob = new Blob([bmpBuffer], { type: 'image/bmp' });
                const downloadUrl = URL.createObjectURL(blob);

                // 创建下载链接
                const a = document.createElement('a');
                a.href = downloadUrl;
                a.download = file.name.replace(/\.jpe?g$/i, '.bmp');
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);

                // 清理URL
                URL.revokeObjectURL(imgUrl);
                URL.revokeObjectURL(downloadUrl);

                console.log('转换完成!');

            } catch (error) {
                console.error('转换失败:', error);
                alert('转换失败: ' + error.message);
            }
        }
    </script>
</body>
</html>

方案2:使用第三方库(html2canvas)

如果需要更复杂的转换功能,可以使用第三方库:

<!-- 先引入html2canvas库 -->
<script src="https://html2canvas.hertzen.com/dist/html2canvas.min.js"></script>

<script>
async function convertJpgToBmpUsingLibrary(jpgFile) {
    return new Promise(async (resolve, reject) => {
        try {
            const img = new Image();
            const reader = new FileReader();

            reader.onload = async (e) => {
                img.onload = async () => {
                    // 创建canvas
                    const canvas = document.createElement('canvas');
                    canvas.width = img.width;
                    canvas.height = img.height;
                    const ctx = canvas.getContext('2d');

                    // 绘制图像
                    ctx.drawImage(img, 0, 0);

                    // 获取图像数据
                    const imageData = ctx.getImageData(0, 0, img.width, img.height);

                    // 使用相同的BMP编码器
                    const encoder = new BMPEncoder(img.width, img.height, imageData.data);
                    const bmpBuffer = encoder.encode();

                    resolve(bmpBuffer);
                };

                img.src = e.target.result;
            };

            reader.readAsDataURL(jpgFile);

        } catch (error) {
            reject(error);
        }
    });
}
</script>

方案3:Node.js后端实现

如果需要服务器端转换,可以使用Node.js:

// 安装依赖:npm install sharp fs
const sharp = require('sharp');
const fs = require('fs');

async function convertJpgToBmp(inputPath, outputPath) {
    try {
        // 读取JPG文件
        const image = sharp(inputPath);

        // 获取图片信息
        const metadata = await image.metadata();

        // 转换为RAW RGB数据
        const rawData = await image
            .raw()
            .toBuffer({ resolveWithObject: true });

        // 创建BMP文件
        await createBMPFile(
            outputPath,
            rawData.info.width,
            rawData.info.height,
            rawData.data
        );

        console.log(`转换完成: ${outputPath}`);
    } catch (error) {
        console.error('转换失败:', error);
    }
}

function createBMPFile(outputPath, width, height, data) {
    return new Promise((resolve, reject) => {
        // 计算行大小(4字节对齐)
        const rowSize = Math.floor((width * 3 + 3) / 4) * 4;
        const fileSize = 54 + rowSize * height;

        // 创建Buffer
        const buffer = Buffer.alloc(fileSize);

        // BMP文件头
        buffer.write('BM', 0); // 文件类型
        buffer.writeUInt32LE(fileSize, 2); // 文件大小
        buffer.writeUInt32LE(0, 6); // 保留字段
        buffer.writeUInt32LE(54, 10); // 像素数据偏移

        // BITMAPINFOHEADER
        buffer.writeUInt32LE(40, 14); // 头大小
        buffer.writeInt32LE(width, 18); // 宽度
        buffer.writeInt32LE(height, 22); // 高度
        buffer.writeUInt16LE(1, 26); // 颜色平面数
        buffer.writeUInt16LE(24, 28); // 每像素位数
        buffer.writeUInt32LE(0, 30); // 压缩方式
        buffer.writeUInt32LE(0, 34); // 图像大小
        buffer.writeInt32LE(0, 38); // 水平分辨率
        buffer.writeInt32LE(0, 42); // 垂直分辨率
        buffer.writeUInt32LE(0, 46); // 调色板颜色数
        buffer.writeUInt32LE(0, 50); // 重要颜色数

        // 像素数据
        let offset = 54;

        // BMP是从下到上存储
        for (let y = height - 1; y >= 0; y--) {
            for (let x = 0; x < width; x++) {
                const srcIndex = (y * width + x) * 3;
                buffer[offset++] = data[srcIndex + 2]; // B
                buffer[offset++] = data[srcIndex + 1]; // G
                buffer[offset++] = data[srcIndex];     // R
            }

            // 填充
            for (let p = width * 3; p < rowSize; p++) {
                buffer[offset++] = 0;
            }
        }

        // 写入文件
        fs.writeFile(outputPath, buffer, (err) => {
            if (err) reject(err);
            else resolve();
        });
    });
}

// 使用示例
convertJpgToBmp('input.jpg', 'output.bmp');

主要特点

前端方案:完全在浏览器中运行,不需要服务器 BMP格式:支持24位真彩色BMP格式 文件下载:转换后自动下载BMP文件 性能优化:使用ArrayBuffer处理二进制数据,性能较好 兼容性:支持所有现代浏览器

注意事项

BMP文件通常比JPG大很多,因为是无压缩格式 前端转换受限于浏览器内存,不适合处理超大图片 BMP格式有多种变体,这里实现的是最常见的24位BMP 转换过程中会丢失EXIF等元数据信息

选择适合你需求的方案即可!

相关推荐