🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

2回答

3997閲覧

SQLite に画像データを保存したい (Windows10, Visual Studio 2019, C#, NET Framework 4.7, System.Data.SQLite

TAKASE_Hiroyuki

総合スコア21

Windows 10

Windows 10は、マイクロソフト社がリリースしたOSです。Modern UIを標準画面にした8.1から、10では再びデスクトップ主体に戻され、UIも変更されています。PCやスマホ、タブレットなど様々なデバイスに幅広く対応していることが特徴です。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

1クリップ

投稿2020/01/05 08:05

編集2020/01/06 05:49

#経緯
Windows10,Visual Studio 2019, C#, .NET Framework 4.7.2, で、アプリケーションを作成しようと考えています。まだ、C# に触り初めてから、2週間程度の初心者です。申し訳ありません。

Visual Studio 2019 に、NuGet を用いて、System.Data.SQLite をインストールしました。これにともない、System.Data.SQLite.EF6, System.Data.SQLite.Generic 等も一緒にインストールされています。

画面には、Form1 だけがあります。また、次のような画像を用意しました。
data.png
この画像の大きさは、ヨコ 500 px タテ 300px です。このような画像の一部を切り取り、SQLite データベースに記録したいと考えています。

#ソースコード

Form1.cs は、次のような内容になっています。

C#

1using System; 2using System.Collections.Generic; 3using System.ComponentModel; 4using System.Data; 5using System.Data.SQLite; 6using System.Data.SQLite.EF6; 7using System.Data.SQLite.Generic; 8using System.Data.SQLite.Linq; 9using System.Drawing; 10using System.Text; 11using System.Threading.Tasks; 12using System.Windows.Forms; 13 14namespace Grph_SQLite 15{ 16 public partial class Form1 : Form 17 { 18 Image im1, im2; 19 20 public Form1() 21 { 22 InitializeComponent(); 23 24 using (var conn = new SQLiteConnection("DataSource=data.db")) 25 { 26 conn.Open(); 27 var comm = conn.CreateCommand(); 28 string sb = "CREATE TABLE IF NOT EXISTS mydata " + 29 " (name TEXT NOT NULL, face BLOB)"; 30 comm.CommandText = sb; 31 comm.ExecuteNonQuery(); 32 conn.Close(); 33 } 34 35 #region 画像を切り取って、im1, im2 に格納し、表示する。 36 37 PictureBox pic = new PictureBox(); 38 pic.Location = new Point(10, 10); 39 pic.Size = new Size(500, 300); 40 pic.Image = Image.FromFile("data.png"); 41 Controls.Add(pic); 42 43 // 元のイメージを切り取って、im1 に貼り付ける 44 im1 = new Bitmap(300, 70); 45 Graphics g = Graphics.FromImage(im1); 46 Rectangle srcRect = new Rectangle(90, 80, 300, 70); 47 Rectangle desRect = new Rectangle(0, 0, 300, 70); 48 g.DrawImage(pic.Image, desRect, srcRect, GraphicsUnit.Pixel); 49 g.Dispose(); 50 51 PictureBox pic1 = new PictureBox(); 52 pic1.Location = new Point(10, 350); 53 pic1.Size = new Size(300, 70); 54 pic1.Image = im1; 55 Controls.Add(pic1); 56 57 // 元のイメージを切り取って、im2 に貼り付ける 58 im2 = new Bitmap(300, 70); 59 g = Graphics.FromImage(im2); 60 srcRect = new Rectangle(90, 180, 300, 70); 61 desRect = new Rectangle(0, 0, 300, 70); 62 g.DrawImage(pic.Image, desRect, srcRect, GraphicsUnit.Pixel); 63 g.Dispose(); 64 65 PictureBox pic2 = new PictureBox(); 66 pic2.Location = new Point(10, 450); 67 pic2.Size = new Size(300, 70); 68 pic2.Image = im2; 69 Controls.Add(pic2); 70 71 // 以上で、im1, im2 に画像データが格納されたことが確認できた。 72 73 #endregion 74 75 #region 画像をデータを、データベースに登録する。 76 77 using (var conn = new SQLiteConnection("DataSource=data.db")) 78 { 79 conn.Open(); 80 81 var comm = conn.CreateCommand(); 82 string str = "insert into mydata(name) values('visual studio1');"; 83 comm.CommandText = str; 84 comm.ExecuteNonQuery(); 85 86 str = "insert into mydata(name) values('visual studio2');"; 87 comm.CommandText = str; 88 comm.ExecuteNonQuery(); 89 90 // ここのところがわからない 91 str = "insert into mydata(name,face) values('visual studio3',@face);"; 92 ImageConverter converter = new ImageConverter(); 93 byte[] fdata = (byte[])converter.ConvertTo(im1, typeof(byte[])); 94 comm.Parameters["face"].Value = fdata; 95 comm.CommandText = str; 96 comm.ExecuteNonQuery(); 97 98 conn.Close(); 99 } 100 101 #endregion 102 } 103 } 104}

概略を説明すると、まず、データベースに接続します(ファイルが無ければ、新しく作ります)。次に、data.png を読み込み、pic.Image に格納し、画面にも表示させます。また、pic.Image の一部を切り取り、im1 と im2 に保存し、それぞれ画面に表示させます。

次にデータベースにレコードを登録します。mydata テーブルのうち、name については、 TEXT NOT NULL ですので、必ず登録しなければなりませんが、face BLOB については、登録しない場合もありえます。

レコードとして、「visual studio1」という name をもつデータと、「visual studio2」という name をもつデータの合計2つのデータが登録できたことは、DB Browser for SQLite を用いて、確認できました。

さて、その下のところで、name と face を両方一度に登録しようと考え、

C#

1// ここのところがわからない 2str = "insert into mydata(name,face) values('visual studio3',@face);"; 3ImageConverter converter = new ImageConverter(); 4byte[] fdata = (byte[])converter.ConvertTo(im1, typeof(byte[])); 5comm.Parameters["face"].Value = fdata; 6comm.CommandText = str; 7comm.ExecuteNonQuery();

と書きました。

#エラーメッセージ

この状態でビルドしても、エラー(コンパイル・エラー)は、ありません。しかし、実際に動かしてみると(F11で一行ずつ実行してみると)、

C#

1comm.Parameters["face"].Value = fdata;

のところで、次のようなランタイム・エラーが発生します。

System.ArgumentOutOfRangeException: 'インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。

パラメーター名:index
System.ArgumentOutOfRangeException
HResult=0x80131502
Message=インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメーター名:index
Source=mscorlib
スタック トレース:
場所 System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
場所 System.Data.SQLite.SQLiteParameterCollection.GetParameter(Int32 index)
場所 System.Data.SQLite.SQLiteParameterCollection.GetParameter(String parameterName)
場所 System.Data.SQLite.SQLiteParameterCollection.get_Item(String parameterName)
場所 Grph_SQLite.Form1..ctor() (C:\hoga\Grph_SQLite\Grph_SQLite\Form1.cs):行 94
場所 Grph_SQLite.Program.Main() (C:\hoga\Grph_SQLite\Grph_SQLite\Program.cs):行 19

#お願い
画像データを、データベースに正しく格納するためには、どのような命令を書けばよいのでしょうか。また、格納されたデータを、プログラムで読むためには、どのような命令を書けばよいのか、についても、合わせて教えていただければ幸いです。

どうか、よろしくお願いいたします。

#追記(解決方法)
以下のような方法で本件は解決しましたので、本文にも追記しておきます。

まず、本文で「ここがわからない」と書いた部分については、次のようにすると画像データを保存できることが確認できました。

C#

1// こうすればできます 2comm.CommandText = "insert into mydata(name,face) values ('visual studio3',@face);"; 3ImageConverter converter = new ImageConverter(); 4byte[] fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 5var param = new SQLiteParameter("@face", System.Data.DbType.Binary); 6param.Value = fdata; 7comm.Parameters.Add(param); 8comm.ExecuteNonQuery();

つぎに、実際のプログラムでは、このような保存作業を、一度に 10000回程度実施する予定ですので、for 文を用いて、10000 回実施してみました。

C#

1 using (var conn = new SQLiteConnection("DataSource=data.db")) 2 { 3 conn.Open(); 4 SQLiteCommand cmd = conn.CreateCommand(); 5 ImageConverter converter = new ImageConverter(); 6 byte[] fdata; 7 SQLiteParameter param; 8 9 for (int i=0; i < 10000; i++) 10 { 11 /* 12 cmd.CommandText = "insert into mydata(name) values (@ID)"; 13 cmd.Parameters.Add(new SQLiteParameter("@ID", "visual studio4")); 14 cmd.ExecuteNonQuery(); 15 */ 16 17 cmd.CommandText = "insert into mydata(name,face) values ('visual studio5',@face);"; 18 fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 19 param = new SQLiteParameter("@face", System.Data.DbType.Binary); 20 param.Value = fdata; 21 cmd.Parameters.Add(param); 22 cmd.ExecuteNonQuery(); 23 24 } 25 26 conn.Close(); 27 }

ところが、100000回、作業を行うのに5分から7分程度必要で、とても実用に堪えないことがわかりました。このことについては、あちこちのページで「トランザクション処理が原因で、非常に速度が遅くなる」と書いてありましたので、実際に作業の前後にトランザクションに関する命令を追加しました。

C#

1var ts = conn.BeginTransaction(); 2<途中の処理> 3ts.Commit();

この2行を追加したプログラムは、次のとおりです。

C#

1 using (var conn = new SQLiteConnection("DataSource=data.db")) 2 { 3 conn.Open(); 4 var ts = conn.BeginTransaction(); 5 6 SQLiteCommand cmd = conn.CreateCommand(); 7 ImageConverter converter = new ImageConverter(); 8 byte[] fdata; 9 SQLiteParameter param; 10 11 for (int i=0; i < 10000; i++) 12 { 13 /* 14 cmd.CommandText = "insert into mydata(name) values (@ID)"; 15 cmd.Parameters.Add(new SQLiteParameter("@ID", "visual studio4")); 16 cmd.ExecuteNonQuery(); 17 */ 18 19 cmd.CommandText = "insert into mydata(name,face) values ('visual studio5',@face);"; 20 fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 21 param = new SQLiteParameter("@face", System.Data.DbType.Binary); 22 param.Value = fdata; 23 cmd.Parameters.Add(param); 24 cmd.ExecuteNonQuery(); 25 26 } 27 28 ts.Commit(); 29 conn.Close(); 30 }

今度は、作業は数秒で終了してしまいました。
以上、報告です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

Parametersは勝手に設定されるものではありません。
どうやってパラメータを追加するかですが
SqliteParameterCollection.Addメソッドを参照してみてください。

投稿2020/01/05 09:00

YAmaGNZ

総合スコア10469

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

TAKASE_Hiroyuki

2020/01/05 10:22

御回答いただき、ありがとうございました。「そもそも」が分かっていなくて、大変恐縮なのですが、YAmaGNZ 様に教えていただいたのは、「Microsoft.Data.Sqlite 3.1」という名前がついているような気がします。私は「System.Data.SQLite 」という名前のものを使っているように思うのですが、このふたつは、同じものなのでしょうか。もし、仮に違うものだとしたら「System.Data.SQLite」でパラメータを適切に追加するためには、どのようにすればよろしいでしょうか。
YAmaGNZ

2020/01/05 14:58

実際に使っているものは違うものかもしれませんが、これらのものはなるべく同じように使用できるようになっています。 ですので、もし違うとしても参考になるかと思います。 一応、紹介したのはMicrosoft.Data.Sqliteネームスペースのものです。 また、「SQLite Parameters Add」など、出てきた文言で検索してみるとよろしいかと思います。
TAKASE_Hiroyuki

2020/01/05 22:20

御回答をいただき、ありがとうございました。 「SQLite Parameters Add BLOB」で検索して出てきたいくつかのページのうち、次のページが大変参考になりました。 http://maeleve.jugem.jp/?eid=19 このページに記載してあった、次の命令文を参考にして、うまく行きそうです。結果については、別途、報告いたします。ありがとうございました。 SQLiteCommand cmd = con.CreateCommand(); cmd.CommandText = String.Format("INSERT INTO T00_FILE_MNGMT (FILE_DT) VALUES (@0);"); SQLiteParameter param = new SQLiteParameter("@0", System.Data.DbType.Binary); param.Value = pic; cmd.Parameters.Add(param);
guest

0

自己解決

YAmaGNZ 様からヒントをいただき、解決できました。解決方法については、本文にも追記しましたが、こちらにも、書いておきます。

まず、次のようなやり方で、画像を保存できました。

C#

1 // こうすればできます 2 comm.CommandText = "insert into mydata(name,face) values ('visual studio3',@face);"; 3 ImageConverter converter = new ImageConverter(); 4 byte[] fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 5 var param = new SQLiteParameter("@face", System.Data.DbType.Binary); 6 param.Value = fdata; 7 comm.Parameters.Add(param); 8 comm.ExecuteNonQuery(); 9

ただし、これだけでは多数回の処理に時間がかかります。そこで、処理の前後にトランザクションに関する命令文を追加すると、速くできることがわかりました。

C#

1 using (var conn = new SQLiteConnection("DataSource=data.db")) 2 { 3 conn.Open(); 4 var ts = conn.BeginTransaction(); 5 6 SQLiteCommand cmd = conn.CreateCommand(); 7 ImageConverter converter = new ImageConverter(); 8 byte[] fdata; 9 SQLiteParameter param; 10 11 for (int i=0; i < 10000; i++) 12 { 13 /* 14 cmd.CommandText = "insert into mydata(name) values (@ID)"; 15 cmd.Parameters.Add(new SQLiteParameter("@ID", "visual studio4")); 16 cmd.ExecuteNonQuery(); 17 */ 18 19 cmd.CommandText = "insert into mydata(name,face) values ('visual studio5',@face);"; 20 fdata = (byte[])converter.ConvertTo(im2, typeof(byte[])); 21 param = new SQLiteParameter("@face", System.Data.DbType.Binary); 22 param.Value = fdata; 23 cmd.Parameters.Add(param); 24 cmd.ExecuteNonQuery(); 25 26 } 27 28 ts.Commit(); 29 conn.Close(); 30 } 31 32

文字情報だけを登録する場合も、画像とともに登録する場合も、ほぼ同じ時間で処理が終了しました。

投稿2020/01/06 05:55

TAKASE_Hiroyuki

総合スコア21

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問