将图片存为二进制流到数据库并展示到前端的实现
使用图片直接存储到数据库中可能会出现以下问题:
1.图片的存储太多了占用数据库的存储空间
2.图片占用内存较大在传输和渲染的情况下会影响应用性能
3.一般情况下是将图片上传云服务器然后数据库存地址,这里讲解的情况只适合图片较少的情景
这里使用的是微信小程序和webapi,sqlserver(理论上思路是一样的)
在微信小程序中我们先需要一个image容器存图片:
<!-- 头像上传 --><view class="form-avatar-section"><view class="avatar-uploader {{avatar ? 'has-avatar' : ''}}" bindtap="chooseAvatar"><image class="avatar-preview" src="{{avatar || '/icons/photo-camera.png'}}" mode="aspectFill" /><view wx:if="{{!avatar}}" class="uploader-mask"><image src="/icons/add-photo.png" class="upload-icon" /><text>点击上传照片</text></view></view></view>
头像的样式你们也可以参考:
/* 头像上传区 */ .form-avatar-section {padding: 48rpx 0;background: #f8fafc;display: flex;justify-content: center; }.avatar-uploader {width: 200rpx;height: 200rpx;border-radius: 50%;position: relative;border: 4rpx solid #e2e8f0;overflow: hidden; }.avatar-preview {width: 100%;height: 100%; }.uploader-mask {position: absolute;width: 100%;height: 100%;background: rgba(241, 245, 249, 0.8);display: flex;flex-direction: column;align-items: center;justify-content: center; }.upload-icon {width: 56rpx;height: 56rpx;margin-bottom: 16rpx;opacity: 0.6; }
以下是在js中的实现,因为我使用表单提交的,所以其中多了个formData, formData中的avatar是存储base64String类型数据,单独的avatar是存储的图片缓存地址
实现获取图片的方法:
// 头像上传chooseAvatar() {var that = this;wx.chooseMedia({ //这个方法微信开发文档有详细讲解count: 1,mediaType: ['image'], //限制只能选中图片sizeType: ['compressed'],success: res => {const tempFile = res.tempFiles[0];// 检查文件大小(1MB = 1024*1024字节)限制2MB及以上图片不能上传if (tempFile.size > 2048 * 1024) {wx.showToast({title: '图片需小于2MB',icon: 'error',duration: 2000});return; // 终止执行}this.setData({avatar: res.tempFiles[0].tempFilePath //将缓存地址赋值})var filePath = res.tempFiles[0].tempFilePath;this.convertToBase64(filePath).then(base => { //这里调用了文件转化方法console.log(base)that.setData({'formData.avatar': base //这里是将base64的数据进行赋值})}).catch(err => {wx.showToast({title: err,icon: 'error'})});},fail: err => {},complete() {}})},
以下是将文件转为base64类型的方法
// 将图片转为base64文件流convertToBase64(tempFilePath) {return new Promise((resolve, reject) => {wx.getFileSystemManager().readFile({filePath: tempFilePath,encoding: 'base64',success: function (res) {resolve(res.data);},fail: function (error) {reject(error)}});})},
传输的数据如下:
在后端只需要使用string类型将数据接收
按照我以下代码的顺序去实现:
首先我们先要有一个将对象带图片加入数据库的sql语句,这里使用的是参数化sql命令
因为图片较大会影响应用性能,所以在这里进行一个文件压缩,如果不需要压缩使用第二张图的方法:
如不需要压缩使用以下方法直接转为二进制流:
如需要压缩,需要使用到的工具类如下:
/// <summary>/// 图片压缩工具类/// </summary>public class ImageCompressor{public byte[] CompressImageFromBase64(string base64Data, long quality = 70L){// 1. 去除Base64头(如"data:image/png;base64,")//var cleanBase64 = base64Data.Split(',')[1];byte[] imageBytes = Convert.FromBase64String(base64Data);// 2. 加载图片并压缩using (var msInput = new MemoryStream(imageBytes))using (var image = Image.FromStream(msInput))using (var msOutput = new MemoryStream()){// 设置JPEG压缩质量(如果是PNG,调整格式)var encoderParams = new EncoderParameters(1);encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);// 获取JPEG编码器var jpegEncoder = GetEncoder(ImageFormat.Jpeg);// 保存压缩后的图片image.Save(msOutput, jpegEncoder, encoderParams);return msOutput.ToArray();}}private ImageCodecInfo GetEncoder(ImageFormat format){var codecs = ImageCodecInfo.GetImageDecoders();foreach (var codec in codecs){if (codec.FormatID == format.Guid)return codec;}return null;}}
当转换成功后,我们将转化的二进制流存入数据库
注意数据库中设计字段一定要为可变长的二进制数(这里使用varbinary(MAX)占用的数据库空间也比较大)
二进制数存储方法如下,其他的数据就使用AddWithValue方式存储
如果你是循环增加的,需要在以下代码前面增加cmd.Parameters.Clear();防止其他问题报错
cmd.Parameters.Clear(); //清除参数
将图片存入数据库后数据如下:
接下来我们需要取出数据进行展示,以下是我们通过sql将数据取出来后,取出的数据要转为byte[]的类型,然后在将byte[]类型转为base64String的数据返回给前端(同样是赋值给string类型)
在我们获取到数据返回给前端后,我们需要将传过来的base64数据通过以下方法进行转化(转化的结果只能作为图片的存储地址,所以有以上结构进行赋值)
const dataURL = `data:image/png;base64,${dataItem.avatar}`;
赋值完成后,就可以显示出图片了