1
votes

I'm trying to make a program that saves images in a database. I've pulled together some info on the net and wrote up code that I think is supposed to work, but fails in a completely unique way on either side (the saving and the loading).

Here's my code, could you tell me what I'm doing wrong?

Load from Database, gives me a memory address access violation error:

    procedure TfrmMain.btnAddPickT4Click(Sender: TObject);
    var
      S : TMemoryStream;
      ikode : integer;
    begin

      if cbxDeelnemerT4.ItemIndex < 0 then
      begin
        MessageDlg('Kies asseblief ''n deelnemer!',mtInformation,[mbOK],1);
        Exit;
      end;

      if OpenPictureDialog1.Execute then
        if FileExists(OpenPictureDialog1.FileName) then
          Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName)
        else
          MessageDlg('Die lêer bestaan nie!',mtError,[mbOK],1);

      ikode := cbxDeelnemerT4.ItemIndex + 1;
      S := TMemoryStream.Create;
      ADOQuery1.Close;
      ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = '+inttostr(ikode);
      ADOQuery1.Open;
      try
        Image1.Picture.Graphic.SaveToStream(S);
        S.Position := 1;
        ADOQuery1.Insert;
        TBLobfield(ADOQuery1.FieldByName('Foto')).LoadFromStream(S);
      finally
        S.Free;
      end;


    end;

Saving to the database. This code doesn't give an error as such, but it just doesn't save the image to the database:

procedure TfrmMain.btnOpenProfileT4Click(Sender: TObject);
var
  S : TMemoryStream;
  ikode : integer;
begin

    ikode := cbxDeelnemerT4.ItemIndex + 1;
  S := TMemoryStream.Create;
  ADOQuery1.Close;
  ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = '+inttostr(ikode);
  ADOQuery1.Open;
  try
    TBlobField(ADOQuery1.FieldByName('Foto')).SaveToStream(S);
    S.Position := 0;
    Image1.Picture.Graphic.LoadFromStream(S);

  finally
   s.Free;
  end;

end;
1
Errm, you seem to be missing an ADOQuery1.Post after your TBLobfield(ADOQuery1.FieldByName('Foto')).LoadFromStream(S);MartynA
what is image1? where is it declared?LionsFan

1 Answers

4
votes

You are calling TGraphic.LoadFromStream() but you are not initializing the TPicture.Graphic property with a valid TGraphic-derived object beforehand, so the property is likely nil, hense the crash.

Also, when saving the TPicture.Graphic to the TMemoryStream, you are skipping the first byte of the graphic when saving it to the DB. All of the bytes are important, so do not skip any of them.

Assuming your images are specifically JPEGs and nothing else (your saving code is not restricting the file type), then try this instead:

procedure TfrmMain.btnAddPickT4Click(Sender: TObject);
var
  S : TMemoryStream;
  ikode : integer;
begin
  if cbxDeelnemerT4.ItemIndex < 0 then
  begin
    MessageDlg('Kies asseblief ''n deelnemer!',mtInformation,[mbOK],1);
    Exit;
  end;

  if not OpenPictureDialog1.Execute then
    Exit;

  Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);
  if not (Image1.Picture.Graphic is TJpegImage) then
    raise Exception.Create('Sorry, only JPG images can be saved in the DB');

  ikode := cbxDeelnemerT4.ItemIndex + 1;
  S := TMemoryStream.Create;
  try
    Image1.Picture.Graphic.SaveToStream(S);
    S.Position := 0;

    ADOQuery1.Close;
    ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = '+IntToStr(ikode);
    ADOQuery1.Open;
    ADOQuery1.Insert;
    try
      TBlobField(ADOQuery1.FieldByName('Foto')).LoadFromStream(S);
      ADOQuery1.Post;
    except
      ADOQuery1.Cancel;
      raise;
    end;
  finally
    S.Free;
  end;
end;

uses
  ..., Jpeg;

procedure TfrmMain.btnOpenProfileT4Click(Sender: TObject);
var
  S : TMemoryStream;
  ikode : integer;
  Jpg: TJPEGImage;
begin
  ikode := cbxDeelnemerT4.ItemIndex + 1;

  ADOQuery1.Close;
  ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = ' + IntToStr(ikode);
  ADOQuery1.Open;

  S := TMemoryStream.Create;
  try
    TBlobField(ADOQuery1.FieldByName('Foto')).SaveToStream(S);
    S.Position := 0;

    Jpg := TJPEGImage.Create;
    try
      Jpg.LoadFromStream(S);
      Image1.Picture.Assign(Jpg);
    finally
      Jpg.Free;
    end;
  finally
    S.Free;
  end;    
end;

But if your images can be other formats besides JPEG, then you need to store the image type in the DB as well so that you can read it back and instantiate the correct TGraphic class type (TBitmap, TJpegImage, TGifImage, TPNGImage, etc) to handle it as needed, eg:

procedure TfrmMain.btnAddPickT4Click(Sender: TObject);
var
  S : TMemoryStream;
  ikode : integer;
begin
  if cbxDeelnemerT4.ItemIndex < 0 then
  begin
    MessageDlg('Kies asseblief ''n deelnemer!',mtInformation,[mbOK],1);
    Exit;
  end;

  if not OpenPictureDialog1.Execute then
    Exit;

  Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);

  ikode := cbxDeelnemerT4.ItemIndex + 1;
  S := TMemoryStream.Create;
  try
    Image1.Picture.Graphic.SaveToStream(S);
    S.Position := 0;

    ADOQuery1.Close;
    ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = '+IntToStr(ikode);
    ADOQuery1.Open;
    ADOQuery1.Insert;
    try
      // this is just an example, there are other ways to do this
      if Image1.Picture.Graphic is TJPEGImage then
        ADOQuery1.FieldByName('FotoType').AsString := 'jpg'
      else if Image1.Picture.Graphic is TPNGImage then
        ADOQuery1.FieldByName('FotoType').AsString := 'png'
      else if Image1.Picture.Graphic is TGIFImage then
        ADOQuery1.FieldByName('FotoType').AsString := 'gif'
      else if Image1.Picture.Graphic is TBitmap then
        ADOQuery1.FieldByName('FotoType').AsString := 'bmp'
      else
        raise Exception.Create('Cannot save unsupported image type to DB');

      TBlobField(ADOQuery1.FieldByName('Foto')).LoadFromStream(S);

      ADOQuery1.Post;
    except
      ADOQuery1.Cancel;
      raise;
    end;
  finally
    S.Free;
  end;
end;

uses
  ..., Jpeg, GifImg, PngImg;

procedure TfrmMain.btnOpenProfileT4Click(Sender: TObject);
var
  S : TMemoryStream;
  ikode : integer;
  Graphic: TGraphic;
  GraphicType: String;
begin
  ikode := cbxDeelnemerT4.ItemIndex + 1;

  ADOQuery1.Close;
  ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = ' + IntToStr(ikode);
  ADOQuery1.Open;

  S := TMemoryStream.Create;
  try
    TBlobField(ADOQuery1.FieldByName('Foto')).SaveToStream(S);
    S.Position := 0;

    GraphicType := ADOQuery1.FieldByName('FotoType').AsString;

    if GraphicType = 'jpg' then
      Graphic := TJPEGImage.Create
      ADOQuery1.FieldByName('FotoType').AsString := 'jpg'
    else if GraphicType = 'png' then
      Graphic := TPNGImage.Create
    else if GraphicType = 'gif' then
      Graphic := TGifImage.Create
    else if GraphicType = 'bmp' then
      Graphic := TBitmap.Create
    else
      raise Exception.Create('Cannot load unsupported image type from DB');

    try
      Graphic.LoadFromStream(S);
      Image1.Picture.Assign(Graphic);
    finally
      Graphic.Free;
    end;
  finally
    S.Free;
  end;    
end;

With that said, you should not be accessing TBlobField directly. Use the TDataSet.CreateBlobStream() method instead, let ADO give you a TStream object that is optimized for accessing ADO blob data, eg:

procedure TfrmMain.btnAddPickT4Click(Sender: TObject);
var
  S : TStream;
  ikode : integer;
begin
  if cbxDeelnemerT4.ItemIndex < 0 then
  begin
    MessageDlg('Kies asseblief ''n deelnemer!',mtInformation,[mbOK],1);
    Exit;
  end;

  if not OpenPictureDialog1.Execute then
    Exit;

  Image1.Picture.LoadFromFile(OpenPictureDialog1.FileName);

  ikode := cbxDeelnemerT4.ItemIndex + 1;

  ADOQuery1.Close;
  ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = '+IntToStr(ikode);
  ADOQuery1.Open;
  ADOQuery1.Insert;
  try
    ...

    S := ADOQuery1.CreateBlobStream(ADOQuery1.FieldByName('Foto'), bmWrite);
    try
      Image1.Picture.Graphic.SaveToStream(S);
    finally
      S.Free;
    end;

    ADOQuery1.Post;
  except
    ADOQuery1.Cancel;
    raise;
  end;
end;

procedure TfrmMain.btnOpenProfileT4Click(Sender: TObject);
var
  S : TStream;
  ikode : integer;
  Graphic: TGraphic;
begin
  ikode := cbxDeelnemerT4.ItemIndex + 1;

  ADOQuery1.Close;
  ADOQuery1.SQL.Text := 'SELECT * FROM DEELNEMERS WHERE Kode = ' + IntToStr(ikode);
  ADOQuery1.Open;

  ...

  Graphic := ...;
  try
    S := ADOQuery1.CreateBlobStream(ADOQuery1.FieldByName('Foto'), bmRead);
    try
      Graphic.LoadFromStream(S);
    finally
      S.Free;
    end;
    Image1.Picture.Assign(Graphic);
  finally
    Graphic.Free;
  end;    
end;