欢迎光临桐乡九九信息网
详情描述
使用JavaScript将JPG格式图片转为BMP格式的完整代码

方案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等元数据信息

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

相关推荐
除了常见的PM2.5,2026年的空气质量预报还会关注哪些新的污染物指标?
除了常见的PM2.5,2026年的空气质量预报还会关注哪些新的污染物指标?
深入解析Python如何利用PyUSB轻松操控USB设备
深入解析Python如何利用PyUSB轻松操控USB设备
如何理解数字服务中的“免费”模式,我们的注意力和数据是否是另一种付费?
如何理解数字服务中的“免费”模式,我们的注意力和数据是否是另一种付费?
2026年,AI交通系统如何改善我们日常的通勤效率与体验?
2026年,AI交通系统如何改善我们日常的通勤效率与体验?
2026年,家中的旧手机和旧电脑除了扔掉,还有哪些环保又有趣的处理方式?
2026年,家中的旧手机和旧电脑除了扔掉,还有哪些环保又有趣的处理方式?
2026年的毕业生求职创业补贴申请流程是怎样的,有哪些关键时间节点?
2026年的毕业生求职创业补贴申请流程是怎样的,有哪些关键时间节点?
如果房屋被认定为危房但不符合补贴条件,还有哪些可行的改造途径或帮扶政策?
如果房屋被认定为危房但不符合补贴条件,还有哪些可行的改造途径或帮扶政策?
在电话中咨询社保缴费记录和年限查询,通常需要提供什么信息?
在电话中咨询社保缴费记录和年限查询,通常需要提供什么信息?
如何查询自己所在城市提供的公益性墓地或骨灰堂的具体位置与申请条件?
如何查询自己所在城市提供的公益性墓地或骨灰堂的具体位置与申请条件?
2026年各地关于“三期”女职工的特殊劳动保护规定,主要差异点在哪里?
2026年各地关于“三期”女职工的特殊劳动保护规定,主要差异点在哪里?
如果服刑前已缴费多年,刑满释放时临近退休,养老金该如何办理?
如果服刑前已缴费多年,刑满释放时临近退休,养老金该如何办理?
2026年大病医保报销范围具体包含哪些医疗费用和药品项目?
2026年大病医保报销范围具体包含哪些医疗费用和药品项目?
远程办公普及下,未来的录用通知是否会新增工作地点与模式条款?
远程办公普及下,未来的录用通知是否会新增工作地点与模式条款?
选购2026年新款扫地机器人,除了吸力还应该重点考察哪些功能?
选购2026年新款扫地机器人,除了吸力还应该重点考察哪些功能?
如何检查并确保自己的手机设置,能够第一时间接收到官方地震预警信息?
如何检查并确保自己的手机设置,能够第一时间接收到官方地震预警信息?
门诊共济政策新调整,2026年起付线、报销比例和年度限额有何变化?
门诊共济政策新调整,2026年起付线、报销比例和年度限额有何变化?
从历年数据看,消费券发放对餐饮、零售行业的拉动效应有何不同?
从历年数据看,消费券发放对餐饮、零售行业的拉动效应有何不同?