VB.NET Access登录系统头像跨PictureBox传递及DBNull转换报错解决
问题根因
- 注册逻辑错误:新增用户记录后未将头像更新到该条记录,而是单独插入了一条仅含头像的新记录,导致对应用户的头像字段为NULL,登录查询时就会触发DBNull转Byte[]的报错
- 缺少非空校验:登录查询时直接将头像字段值强转为Byte(),未提前判断字段是否为NULL
- Form2头像赋值逻辑写反:Form2加载时将空的头像控件值赋值给全局变量,而非用全局变量给控件赋值,导致Form2始终显示空头像
- 头像存储缺陷:使用
MemoryStream.GetBuffer()获取字节数组,会包含冗余的未使用字节,应该用ToArray()获取准确的流内容
修复代码
修改Form1的saveimage方法
改为更新对应用户的头像字段,而非插入新记录
Private Sub saveimage(userName As String) If PictureBox1.Image Is Nothing Then Throw New Exception("请先选择头像") End If Dim arrimage() As Byte Using mstream As New System.IO.MemoryStream If PictureFormat.ToLower = ".png" Then PictureBox1.Image.Save(mstream, System.Drawing.Imaging.ImageFormat.Png) ElseIf PictureFormat.ToLower = ".jpg" Then PictureBox1.Image.Save(mstream, System.Drawing.Imaging.ImageFormat.Jpeg) End If ' 用ToArray代替GetBuffer,获取准确的字节内容 arrimage = mstream.ToArray() End Using ' 改用Update语句更新对应用户的头像字段 Using con As New OleDbConnection(cnStr), cmd As New OleDbCommand("Update TBLoginPic set Imagen = @Imagen where Usuario = @Usuario", con) With cmd .Parameters.Add("@Imagen", OleDbType.Binary).Value = arrimage .Parameters.Add("@Usuario", OleDbType.VarChar).Value = userName con.Open() .ExecuteNonQuery() End With End Using End Sub
修改Form1的注册按钮逻辑
传入当前注册的用户名更新头像,增加必填校验
Private Sub BtnRegistrarse_Click(sender As Object, e As EventArgs) Handles BtnRegistrarse.Click If String.IsNullOrWhiteSpace(TBusuario.Text) OrElse String.IsNullOrWhiteSpace(TBcontraseña.Text) Then MsgBox("用户名和密码不能为空") Exit Sub End If If PictureBox1.Image Is Nothing Then MsgBox("请先选择头像") Exit Sub End If Me.TBLoginPicBindingSource.AddNew() Me.TBLoginPicBindingSource.EndEdit() Me.TBLoginPicTableAdapter.Update(Me.DBLoginPicDataSet) Try saveimage(TBusuario.Text) Catch ex As Exception MessageBox.Show(ex.Message) Exit Sub End Try MsgBox("注册成功,头像已保存") End Sub
修改Form1的登录按钮逻辑
增加DBNull校验,避免强转报错
Private Sub BtnLoguearse_Click(sender As Object, e As EventArgs) Handles BtnLoguearse.Click If Me.TBLoginPicTableAdapter.BuscarDatos(Me.DBLoginPicDataSet.TBLoginPic, TBusuario.Text, TBcontraseña.Text) Then Dim dt As DataTable Try dt = GetDataByName(TBusuario.Text) If dt.Rows.Count = 0 Then MsgBox("未查询到用户信息") Exit Sub End If Catch ex As Exception MessageBox.Show(ex.Message) Exit Sub End Try TBusuario.Text = dt(0)("Usuario").ToString ' 增加DBNull判断 If Not IsDBNull(dt(0)("Imagen")) Then Dim arrimage = DirectCast(dt(0)("Imagen"), Byte()) Using mstream As New System.IO.MemoryStream(arrimage) PictureBox1.Image = Image.FromStream(mstream) End Using Else ' 可提前添加默认头像到项目资源,此处赋值默认头像 PictureBox1.Image = My.Resources.DefaultAvatar End If Form2.Show() _selectedImage = PictureBox1.Image Me.Close() Else MsgBox("账号或密码错误") End If End Sub
修改Form2的Load事件逻辑
修正头像赋值方向
Private Sub Form2_Load(sender As Object, e As EventArgs) Handles MyBase.Load ' 不需要重复加载表数据可以删除此行,减少冗余查询 ' Me.TBLoginPicTableAdapter.Fill(Me.DBLoginPicDataSet.TBLoginPic) If _selectedImage IsNot Nothing Then PBPerfil.Image = _selectedImage End If End Sub
优化头像选择逻辑
避免用户点取消时报错
Private Sub FillPictureBoxFromFile() With OpenFileDialog1 .Filter = "(*.jpg)|*.jpg|(*.png)|*.png" .RestoreDirectory = True .Title = "Select a file to open" If .ShowDialog = DialogResult.OK Then PictureBox1.Image = Image.FromFile(.FileName) PictureFormat = Path.GetExtension(.FileName) End If End With End Sub
其他优化建议
- 数据库连接路径不要写死,改用相对路径或者配置文件存储,避免换环境就无法访问数据库
- 密码不要明文存储在数据库,建议加盐哈希后存储,提升安全性
- 全局变量传值可以改成Form2构造函数传参,更安全,避免全局变量被意外修改
- 所有实现了IDisposable接口的对象(比如MemoryStream、数据库连接)都用Using包裹,自动释放资源避免内存泄漏
内容的提问来源于stack exchange,提问作者THEwed123wet
相关产品推荐
相关产品推荐

