如何拼接两个Base64图片字符串并还原为完整图片文件?
问题分析与解决方案
你的核心问题有两个:一是代码里的低级拼写错误,二是直接拼接两个独立JPEG文件的字节数组无法生成合法图片——因为每个JPEG都是完整的文件格式,包含SOI(起始标记)和EOI(结束标记),拼接后会变成两个JPEG的堆叠,不是单一合法的图片文件。
第一步:修正代码里的低级错误
你的代码中data2的转换写错了,用了convert而不是convert2,导致两个字节数组重复,先修正这个:
// 原错误代码 byte[] data2 = Convert.FromBase64String(convert); // 修正为 byte[] data2 = Convert.FromBase64String(convert2);
但即使修正了这个,直接拼接JPEG字节数组依然无法得到正确图片,下面是两种可行的解决方案:
方案一:前端先合并Canvas再传输(优先推荐)
既然是同一个页面的不同区域,直接在前端把两个canvas合并成一个大canvas,再转成base64。如果合并后的base64字符串超过32K,再对这个单一大base64进行分块传输,而不是分块截图。
修改前端JS代码:
// 新增合并两个Canvas的函数 async function MergeAndGetFullScreenshot(headerId, footerId) { // 获取两个区域的canvas const headerCanvas = await html2canvas(document.getElementById(headerId), { allowTaint: true, useCors: true, background: "#00FFFFFF" }); const footerCanvas = await html2canvas(document.getElementById(footerId), { allowTaint: true, useCors: true, background: "#00FFFFFF" }); // 创建一个大Canvas,高度是两个Canvas的总和,宽度取较大的那个 const fullWidth = Math.max(headerCanvas.width, footerCanvas.width); const fullHeight = headerCanvas.height + footerCanvas.height; const fullCanvas = document.createElement('canvas'); fullCanvas.width = fullWidth; fullCanvas.height = fullHeight; const ctx = fullCanvas.getContext('2d'); // 绘制头部和尾部到大Canvas ctx.drawImage(headerCanvas, 0, 0); ctx.drawImage(footerCanvas, 0, headerCanvas.height); // 转成base64,如果超过32K,就分块返回 const base64 = fullCanvas.toDataURL('image/jpeg', 0.5); const chunkSize = 30000; // 留余量,小于32K const chunks = []; for (let i = 0; i < base64.length; i += chunkSize) { chunks.push(base64.slice(i, i + chunkSize)); } return chunks; }
Blazor后端代码:
async Task SaveScreenShotToFile() { // 获取分块的base64数组 var base64Chunks = await JS.InvokeAsync<List<string>>("MergeAndGetFullScreenshot", "divRaHeader", "divRaFooter"); if (base64Chunks == null || !base64Chunks.Any()) return; // 拼接所有分块 string fullBase64 = string.Join("", base64Chunks); string cleanBase64 = fullBase64.Replace("data:image/jpeg;base64,", string.Empty); byte[] imageData = Convert.FromBase64String(cleanBase64); var filepath = @"C:\VS2022\AppMain\wwwroot\Image.jpg"; await File.WriteAllBytesAsync(filepath, imageData); }
方案二:后端接收独立图片,用图片处理库合并(备选)
如果必须分块获取截图(比如区域太大无法前端合并),可以在后端使用图片处理库(如SixLabors.ImageSharp)将两张图片垂直拼接成一张。
示例(先安装NuGet包:SixLabors.ImageSharp):
using SixLabors.ImageSharp; using SixLabors.ImageSharp.Processing; async Task SaveScreenShotToFile() { string headerBase64 = await JS.InvokeAsync<string>("GetDivScreenShot", "divRaHeader"); string footerBase64 = await JS.InvokeAsync<string>("GetDivScreenShot", "divRaFooter"); if (string.IsNullOrEmpty(headerBase64) || string.IsNullOrEmpty(footerBase64)) return; // 解码两张图片 byte[] headerBytes = Convert.FromBase64String(headerBase64.Replace("data:image/jpeg;base64,", string.Empty)); byte[] footerBytes = Convert.FromBase64String(footerBase64.Replace("data:image/jpeg;base64,", string.Empty)); using (var headerImage = Image.Load(headerBytes)) using (var footerImage = Image.Load(footerBytes)) { // 创建合并后的图片 int fullWidth = Math.Max(headerImage.Width, footerImage.Width); int fullHeight = headerImage.Height + footerImage.Height; using (var fullImage = new Image<Rgba32>(fullWidth, fullHeight)) { // 绘制头部和尾部 fullImage.Mutate(ctx => { ctx.DrawImage(headerImage, new Point(0, 0), 1f); ctx.DrawImage(footerImage, new Point(0, headerImage.Height), 1f); }); // 保存到文件 var filepath = @"C:\VS2022\AppMain\wwwroot\Image.jpg"; await fullImage.SaveAsJpegAsync(filepath); } } }
注:System.Drawing.Common在.NET Core/.NET 5+的非Windows平台有兼容性问题,推荐使用跨平台的ImageSharp。
内容的提问来源于stack exchange,提问作者Ben Junior
相关产品推荐
相关产品推荐

