Laden...

Bilder (byte[]) in eine MSSQL DB speichern

Erstellt von Lakustus vor 7 Jahren Letzter Beitrag vor 7 Jahren 4.810 Views
L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren
Bilder (byte[]) in eine MSSQL DB speichern

verwendetes Datenbanksystem: MSSQL mit MS SQL Server 2008

Hallo alle,

hoffe jemand kann mir etwas erklären wie man sowas durchführt. Ich habe eine List<byte[]> und
möchte diese (sind 10 Einträge) in 10 Spalten der DB abspeichern. Hierfür verwende ich folgenden code:


foreach (byte[] b in img)
                {
using (SqlConnection sql = new SqlConnection("Data Source=" + "10.0.1.1" + @",1433; Network Library=DBMSSOCN; Initial Catalog=mydb; User ID=" + "me" + @"; Password=" + "mypw" + ";"))
                    {
                        sql.Open();
                        SqlCommand com = new SqlCommand("INSERT INTO dbo.benutzer ([" + i + "]) VALUES(@b) WHERE BenutzerID = " + id + "", sql);
                        SqlParameter param = new SqlParameter("@b", SqlDbType.Image);
                        param.Value = b;
                        com.ExecuteNonQuery();
                        i++;
                    }
}

Hierbei erhalte ich die Fehlermeldung > Fehlermeldung:

"SqlException "Die Variable '@b' muss deklariert werden.

Bin noch sehr sehr newbie und verstehe diese Materie nicht so sehr. Und sorry für den unschönen Stil.

Habs auch mal (unter vielen anderem^^) damit versucht und es werden zwar DB-Einträge vorgenommen aber jede Spalte
hat exakt den selben Hex-Wert obwohl es sich jeweils um andere Bilder handelt. (?)


foreach (byte[] b in img)
                {
                    using (SqlConnection sql = new SqlConnection("Data Source=" + "10.0.1.1" + @",1433; Network Library=DBMSSOCN; Initial Catalog=mydb; User ID=" + "me" + @"; Password=" + "mypw" + ";"))
                    {
                        SqlCommand com = new SqlCommand("UPDATE dbo.benutzer SET[" + i + "] = @b WHERE BenutzerID = " + id + "", sql);
                        SqlParameter param = new SqlParameter("@b", SqlDbType.Image);
                        com.Parameters.Add("@b", SqlDbType.Image, 10).Value = b;
                        sql.Open();
                        SqlDataReader rdr = com.ExecuteReader();
                        i++;
                    }
                }

16.807 Beiträge seit 2008
vor 7 Jahren

[Artikelserie] SQL: Parameter von Befehlen

Für jedes Byte eine SQL Verbindung zu öffnen ist natürlich Quatsch.
Ein Byte ist auch kein SqlDbType.Image. Die Summe der Bytes macht es zum Image.
Was das i sein soll kann man hier nur raten.

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

Hi Abt 😃

danke für's antworten!

ich weiss das es quatsch ist, deshalb ja: "Und sorry für den unschönen Stil." Es geht mir erstmal nur um die Funktionalität und danach wollt ich die Aufteilung machen 😮)

Das "i" ist unwichtig deshalb hab ichs unkommentiert gelassen. Stellt einfach nur den Spaltennamen dar.. Die DB hat 10 Spalten die jeweils 1-10 heissen ;P

Ist alles Testumgebungstechnisch.

Habs auch mit SqlDbType.VarBinary und anderem versucht - entweder gabs eine Fehlermeldung oder die Einträge in der DB hatten immer den selben Wert.

Wie gesagt hab ich kaum eine Ahnung mit Datenbanken und versuche nun Bilder die serialisiert und in einer List<byte[]> stecken, in eine DB zu kriegen (und wieder auszulesen). Da ich es nicht hinbekomme, ersuche ich Hilfe :>

16.807 Beiträge seit 2008
vor 7 Jahren

Und sowas wieHow to save image in database using C# hilft Dir nicht?
Wenn Du einen Eintrag in eine DB machst, dann muss natürlich das gesamte ByteArray übertragen werden und nicht nur x Bytes.
Das macht doch wenig sinn 😉

Es ist einfach die (unüberlegte) Logik, die nicht stimmt.

PS: temporärer Code hält sich ewig.
Gewöhn Dir das also gar nicht erst an.

4.931 Beiträge seit 2008
vor 7 Jahren

Hallo,

der eigentliche Fehler ist, daß du zwar eine SqlParameter-Variable erstellst, diese aber nicht dem SqlCommand hinzufügst:


com.Parameters.Add(param);

Ups, sehe gerade, daß du ja das in deinem 2. Code im Eingangsbeitrag schon machst...
Aber der Code macht auch wenig Sinn, denn du führst das UPDATE-Command gar nicht korrekt aus (wie beim "INSERT").

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

Hallo und danke für eure Antworten!

habe es hiermit hinbekommen (musste "leider" den Datentyp der Spalte in der DB auf image stellen (bin mir aber gerade nicht sicher ob ich es auch mit varbinary versucht hab, hmm, mach ich mal nebenbei)):


public static bool InsertData(List<byte[]> img, int id)
        {
            int i = 1;
            try
            {
                foreach (byte[] b in img)
                {
                    using (SqlConnection sql = new SqlConnection("Data Source=" + "10.0.1.1" + @",1433; Network Library=DBMSSOCN; Initial Catalog=myDB; User ID=" + "me" + @"; Password=" + "myPW" + ";"))
                    {
                        SqlCommand com = new SqlCommand("UPDATE dbo.benutzer SET[" + i + "] = @b WHERE BenutzerID = " + id + "", sql);
                        SqlParameter param = com.Parameters.Add("@b", SqlDbType.VarBinary);
                        param.Value = b;
                        sql.Open();
                        SqlDataReader rdr = com.ExecuteReader();
                        i++;
                    }
                }
                return true;
            }
            catch (Exception e)
            {
                return false;
            }
        }

Klar, die Schönheitsfehler mit dem "immerwieder "open" und close" ect. ändere ich noch bei nächster Gelegenheit. Ging ja primär erstmal um's hinbekommen 😃

Jetzt versuche ich die Daten (Bilder) wieder auszulesen um (mit einem vorgegebenen Bild) miteinnander auf Gleichheit zu prüfen. Das wirklich doofe dabei ist, dass der "Vergleichsalgorithmus" unbedingt ein zweidimensionales byte[] haben will. Wie ließt man bloß die DB aus und weist die Daten(Bilder) einem byte[][] zu? Und gibt es bei solchen Umwandlungen Verluste sodass das Bild nicht mehr 100% original ist und dadurch ein Vergleich erwschwert wird?.

Habe folgendes versucht:


string s = "SELECT * FROM dbo.benutzer";
            using (SqlConnection sql = new SqlConnection("Data Source=" + "10.0.1.1" + @",1433; Network Library=DBMSSOCN; Initial Catalog=myDB; User ID=" + "me" + @"; Password=" + "myPW" + ";"))
            {
                SqlCommand com = new SqlCommand(s, sql);
                com.CommandTimeout = 10;
                SqlDataReader rdr;
                sql.Open();
                rdr = com.ExecuteReader();
                while (rdr.Read())
                {
                    for (int i = 0; i < rdr.FieldCount; i++)
                    {
                       if(rdr[i] is byte[])
                            Template2Array[i] = ((byte[][])rdr[i]);
                    }
                }
             }

Erhalte entweder Konvertierungsfehler (kann nicht explizit von byte[] in byte[][] konvertiert werden) oder "System.NullReferenceException".

Mir ist klar das ich Denkfehler und typische Anfängerfehler mache - bitte helft mir was zu lernen 😃

D
985 Beiträge seit 2014
vor 7 Jahren

Gegenfrage, die dir die Problematik mit dem Konvertieren näherbringen soll:

Erstelle bitte aus dieser eindimensionalen Folge genau die zweidimensionale Struktur, die ich im Kopf habe.

1,2,3,4,5,6,7,8,9

Die korrekte Antwort lautet: Unlösbar, weil ich nicht weiß was du im Kopf hast.

16.807 Beiträge seit 2008
vor 7 Jahren

ändere ich noch bei nächster Gelegenheit.

Dinge, die man "bei nächster Gelgenheit" änder will, werden in 99% der Fälle nie geändert.
10 Minuten investieren und gleich richtig machen als später 100 Minuten investieren, weil man viel mehr anfassen muss.

but thats not my business

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

och Abt 😃

1.) um das "besser programmieren" geht es doch in dem Thread garnicht, sondern um eine Problemumsetzung
2.) traue keiner Statistik die du nicht selbst gefälscht hast 😄 ich werde das schon noch machen, sobald ich das Wissen um die Umsetzung des Problems verstanden/umgesetzt habe.

Sir Rufo: du verstehst nicht was mein Problem ist? oder willst du sagen dass eine Konvertierung von eindimensionalen Arrays in zweidimensionale nicht möglich ist? 😛

O
79 Beiträge seit 2011
vor 7 Jahren

Die Konvertierung ist wohl möglich.

Abt verlangt von dir nur, das eindimensionale Array in das zweidimensionale Array umzuwandeln, von dem er dir nicht verrät, wie es aussieht.

Denn genau das verlangst du vom SQL-Server bzw. deinem Programm auch, weshalb das ganze mit einer unwillig wirkenden Fehlermeldung antwortet.

D
985 Beiträge seit 2014
vor 7 Jahren

Sir Rufo: du verstehst nicht was mein Problem ist? oder willst du sagen dass eine Konvertierung von eindimensionalen Arrays in zweidimensionale nicht möglich ist? 😛

Natürlich kann man ein eindimensionales Array in einem zweidimensionales Array irgendwie abbilden - aber wie denn genau? Es gibt da auf jeden Fall keine Eindeutigkeit. Und genau das ist der Grund für diese Fehlermeldung.

Mein Beispiel könnte z.B. wie folgt interpretiert werden:

1,2
3,4
5,6
7,8
9

oder

1,2,3
4,5,6
7,8,9

oder, oder, oder, ...

Und an der Mindreader-API arbeitet MS noch - also muss dein Code leider eindeutig sein, was er aber eben nicht ist

16.807 Beiträge seit 2008
vor 7 Jahren

Abt verlangt von dir nur, das eindimensionale Array in das zweidimensionale Array umzuwandeln, von dem er dir nicht verrät, wie es aussieht.

Öh.. nö. War - wie man am Zitat sehen kann - auf die Connection und dem Umgang dessen bezogen.

5.657 Beiträge seit 2006
vor 7 Jahren

Wahrscheinlich ist hier mit der Umwandlung in ein 2D-Array die Representation des Bildes als Bitmap gemeint, d.h. die Speicherung der Farbwerte je Pixel. Dazu müßte man ersteinmal das eindimensionale Byte-Array in ein Bitmap umwandeln, und dann kann man dort die Farbwerte für jeden Pixel abfragen und in einem 2D-Array abspeichern.

Weeks of programming can save you hours of planning

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

Guten Morgen und vielen Dank für all eure Antworten!

Die Sache ist die, dass ich Bilder aus der DB auslesen und hiermit auf Gleichheit testen muss:


variable = Matcher.Identify(Template1, size, Template2, Template2Size, Template2Num, 5000, out MatchIndex);

(Matcher.Identify(byte[] Template1, int Template1Size, byte[][] Template2Array, int Template2Num, ect.)

Template1 und dessen size ist das Bild was gegeben ist und Template2 ein byte[][] zB. mit Daten ausgelesen aus einer DB.

Meine erste Frage dazu lautet "wieso ist das zweite überhaupt ein zweidimensionales Array? und die nächste Frage dann "wie lese ich die DB in das zweidimensionale Array ein"?

D
985 Beiträge seit 2014
vor 7 Jahren

Beide Fragen sind ohne weitere Kenntnis der Methode bzw. wie denn bitte diese Arrays gefüllt werden sollen nicht zu beantworten.

Frage den Ersteller dieser Methode entweder direkt oder indirekt (indem du in die evtl. vorhandene Dokumentation schaust).

74 Beiträge seit 2014
vor 7 Jahren

Mal nebenbei: Ein byte[][] ist kein 2D-Array, sondern ein Jagged Array. Ein 2D-Array wäre byte[,]. Das sind zwei unterschiedliche Paar Stiefel.

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

Die dazugehörige Methode ist:


public UFM_STATUS Identify(byte[] Template1, int Template1Size, byte[][] Template2Array, int[] Template2SizeArray, int Template2Num, int Timeout, out int MatchTemplate2Index)
		{
			IntPtr[] array = new IntPtr[Template2Num];
			for (int i = 0; i < Template2Num; i++)
			{
				array[i] = Marshal.AllocHGlobal(Template2SizeArray[i]);
				Marshal.Copy(Template2Array[i], 0, array[i], Template2SizeArray[i]);
			}
			int result = _UFMatcher.UFM_Identify(this.m_hMatcher, Template1, Template1Size, array, Template2SizeArray, Template2Num, Timeout, out MatchTemplate2Index);
			for (int i = 0; i < Template2Num; i++)
			{
				Marshal.FreeHGlobal(array[i]);
			}
			return (UFM_STATUS)result;
		}

Hoffe das hilft dir?

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

das mit dem 2D-Array - in der dll steckt auch diese Methode noch drin:


public UFM_STATUS Identify(byte[] Template1, int Template1Size, byte[,] Template2Array, int[] Template2SizeArray, int Template2Num, int Timeout, out int MatchTemplate2Index)
		{
			return (UFM_STATUS)_UFMatcher.UFM_Identify(this.m_hMatcher, Template1, Template1Size, Template2Array, Template2SizeArray, Template2Num, Timeout, out MatchTemplate2Index);
		}

Grüße

D
985 Beiträge seit 2014
vor 7 Jahren

Was du uns zeigst ist eine Wrapper-Methode für eine DLL. Daraus kann man leider nichts ableiten.

Es riecht ein wenig nach Suprema UniFinger. Das müsstest du aber besser wissen (und hättest du auch direkt im resten Post schon schreiben sollen).

Dann besorge dir doch die Dokumentation dafür und du solltest wissen, wie du das Bild in so ein Byte-Array bekommst.

L
Lakustus Themenstarter:in
25 Beiträge seit 2016
vor 7 Jahren

Jau, es geht um Suprema Fingerprinter. Die Doku bzw. dene ihr Guide hab ich ja aber der hilft ja wirklich wenig.

Zum Problem selbst: habe (glaub ich jedenfalls:D) das Auslesen der DB bzw. die byte[] in ein byte[][] zu kriegen hinbekommen.


//Template2 ist byte[][]

//während die DB durchgelesen wird
while (rdr.Read())
                {
                    for (int i = 0; i < rdr.FieldCount; i++)
                    {
                        if (rdr[i] is byte[] && rdr[i] != null)
                        {
                            Template2[i - 5] = (byte[])rdr[i];
                            Template2SizeArray[i - 5] = Template2[i - 5].Length;
                            //Template2Array.Add((byte[])rdr[i]);
                        }
                    }
               }

Grüße