SQL INSERT größere Datei mit Progressbar möglich?

  • VB.NET

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von siycah.

    SQL INSERT größere Datei mit Progressbar möglich?

    Hallo Zusammen,

    ich hätte eine Frage zu dem upload von einer gößeren Datei in SQL

    Ist es möglich die Datei stückweise hochzuladen sodass man den momentanen upload stand abfangen kann und in einer progressbar sichtbar machen kann?

    Oder gibt es irgendwelche Befehle, womit man die hochgeladenen Bytes zurückbekommt?

    Vielen Dank für die Hilfe :)
    ja klar, wird in varbinary geschrieben

    VB.NET-Quellcode

    1. Dim bufferFile() As Byte
    2. bufferFile = System.IO.File.ReadAllBytes(Filepath)
    3. Dim Command As New SqlCommand("Update Database set [file]=@file where [Index]='" & INT_NO_MODIFY & "'") With {.Connection = VAR.Connection}
    4. Command.Parameters.AddWithValue("@file", bufferFile)
    5. Command.ExecuteNonQuery()
    Ich glaube nicht, dass du wirklich einen Fortschritt darstellen kannst, da es eine einzelne Operation ist, wie @ErfinderDesRades bereits erwähnt hat.
    Ein Workaround wäre, dass du ein Fortschrittbalken ohne Fortschritt (Marquee-Effekt, z.B.) darstellst.

    Allerdings muss man auch dazu sagen, dass es meistens besser ist, wenn man SQL-Anweisungen kurz und knapp hält.
    Wenn du einen Fortschritt anzeigen möchtest, dann könntest du dir zuerst alle Elemente auslesen, die du updaten willst und dann einer nach dem anderen mit dem neuen Wert versehen.

    Dann könntest du einfach den Fortschritt in % anzeigen.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Wäre es vielleicht möglich die bytes von der datei zu splitten und nacheinander in das gleiche feld zu jagen? ich habe es mal getestet, dann kommt aber irgend ne fehlermeldung, dass das irgendwas fehlt... also einfach mit sql concat scheint varbinary nicht zu gehen.

    oder ein anderer Ansatz wäre vielleicht, die datei hochzuladen und in einem sepparatem thread die bereits geschriebene dateigröße (select) alle 2 sek zu ermitteln? was meint ihr?
    Wie groß ist solch eine Datei und wie lange dauert das Speichern der selbigen?

    Meist ist der Aufwand einen möglichst genauen Fortschritt anzuzeigen völlig unnötig, weil schneller (meist sogar gegenteilig) geht es dadurch ja nicht. Es könnte ja bereits reichen nach jeder Datei den Fortschritt anzuzeigen.

    Zumal das Aufbauen einer Verbindung zur Datenbank auch wieder Zeit kostet. Diese zu opfern um einen Zwischenstand anzeigen zu können sollte man sich gut überlegen.

    Außerdem sollte man sich überlegen, ob man überhaupt Dateien IN der Datenbank speichern will, denn z.B. der MS SQL Server bietet ein Feature namens „File Tables“ die sich hier sehr anbieten würde.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen

    r0tzi schrieb:

    ? ich habe es mal getestet, dann kommt aber irgend ne fehlermeldung, dass das irgendwas fehlt... also einfach mit sql concat scheint varbinary nicht zu gehen.


    Eine genaue Fehlermeldung würde da schon helfen.

    r0tzi schrieb:

    oder ein anderer Ansatz wäre vielleicht, die datei hochzuladen und in einem sepparatem thread die bereits geschriebene dateigröße (select) alle 2 sek zu ermitteln? was meint ihr?


    Nein, das wird nicht klappen.
    Warum willst du überhaupt Dateien in eine Datenbank hochladen?

    Wäre doch viel sinnvoller, die Dateien auf der Festplatte in einem Ordner zu lagern und dann entweder nach Zeitraum X zu löschen, oder bis man es selbst löscht.
    Dann kannst du die Dateipfade in die Datenbank speichern. So wird das mWn auch überall anders noch gemacht.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)

    mrMo schrieb:

    Wie groß ist solch eine Datei und wie lange dauert das Speichern der selbigen?


    kann bis zu 30 mb sein, dauert über 1 minute (vpn und wlan)

    mrMo schrieb:

    Außerdem sollte man sich überlegen, ob man überhaupt Dateien IN der Datenbank speichern will, denn z.B. der MS SQL Server bietet ein Feature namens „File Tables“ die sich hier sehr anbieten würde.


    ist in der firma deaktiviert... warum wohl :D

    siycah schrieb:

    Eine genaue Fehlermeldung würde da schon helfen.

    habe ich leider nicht mehr und den code verworfen -.-

    siycah schrieb:

    Nein, das wird nicht klappen.

    schon ausprobiert? Weil einen Code fürs runterladen mit progress habe ich, da wird die gesamte größe ausgelesen und dann abgeglichen im memorystream.

    siycah schrieb:

    Warum willst du überhaupt Dateien in eine Datenbank hochladen?

    Wäre doch viel sinnvoller, die Dateien auf der Festplatte in einem Ordner zu lagern und dann entweder nach Zeitraum X zu löschen, oder bis man es selbst löscht.
    Dann kannst du die Dateipfade in die Datenbank speichern. So wird das mWn auch überall anders noch gemacht.


    ich finde SQL einfach besser, da ich eine schöne datei history anlegen kann und jegliche änderungen somit nachverfolgen wer was gemacht hat. <- ja ich weiss geht auch auf der festplatte.. ^^ ich brauch herausforderungen xD

    Zitierfehler korrigiert ~VaporiZed

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    r0tzi schrieb:

    ich finde SQL einfach besser, da ich eine schöne datei history anlegen kann und jegliche änderungen somit nachverfolgen wer was gemacht hat. <- ja ich weiss geht auch auf der festplatte.. ich brauch herausforderungen xD


    Klingt für mich, als würdest du das Rad neu erfinden und ein Tool für etwas missbrauchen wollen, wofür es nicht gedacht ist.

    Wenn du wirklich eine Änderungshistorie haben willst, dann nimm etwas wie Git - oder eine Suchmaschine.

    Ich habe vor ewigen Monden mal einen Dateiindexer geschrieben - scheint als würdest du etwas ähnliches machen wollen.

    Parse deine Dateien - bspw. mit Tika.NET, baue ein Objekt auf und schiebe das in einen Indexer. Ich finde Elasticsearch genial für sowas.
    Du könntest natürlich auch Lucene nutzen und dir selbst was bauen, wenn du die Herausforderung willst.

    Dann - wenn du wirklich willst - füge noch die Datei selber in die Suchmaschine.
    Persönlich empfehle ich dir immer noch - aus diversen Gründen - die Dateien selber abzuspeichern. Metadaten und Änderungsdaten/Patchfiles kannst du nach wie vor in die Suchmaschine speichern.
    Dafür kannst du bei jedem Upload ein Delta von den Dateien machen und das Delta entsprechend indizieren.
    Wenn du die Dateien herunterladen willst, fügst du einfach die Deltas zu den Dateien.

    Wenn du Elasticsearch nehmen willst, haben die eine richtig coole .Net-API mit der du auch asynchron arbeiten kannst.
    Dann kannst du zeitgleich (Anzahl Kerne in CPU - 2) Dateien gleichzeitig parsen und wegindizieren
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)
    Jenni Jenni,

    Ja, es ist Möglich. Du musst einfach das Protokoll neu implementieren und dann einfach Byte für Byte in den Stream schreiben und nach dem Flush ein Invoke senden um die Progressbar um 1 zu erhöhen. Sau viel Arbeit und Gefahr für Sicherheitslücken wenn man es schlampig macht aber das ist jetzt der weg der mir so spontan einfallen würde.

    Altanative, ein Application Server hosten auf dem ein Webdienst läuft wo du die Datei in Chunks aufteilst und dann am Ende über die Webanwendung hochlädst. Dann kannst du ein HTTP Upload in der Progressbar anzeigen. Das ganze lässt sich auch mit PHP realisieren, muss kein kompliziertes ASP.NET mit Windows Server sein. Hoffe ich konnte da bissen helfen.

    LG, Jenni Herbrich
    Hallo,

    hier der funktionierende Code für den Upload, die Progressbar funktioniert und das UI hängt nicht.

    VB.NET-Quellcode

    1. Dim WithEvents _BackgroundWorker As New BackgroundWorker
    2. Private Sub _BackgroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles _BackgroundWorker.DoWork
    3. Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
    4. worker.WorkerReportsProgress = True
    5. e.Result = SendFileToSQL(worker, e)
    6. End Sub
    7. Private Function SendFileToSQL(ByVal worker As BackgroundWorker, ByVal e As DoWorkEventArgs) As Byte()
    8. Dim progressPercentage As Integer
    9. Dim sentBytes As Long
    10. Dim totalSize As Long
    11. SQLConOpen()
    12. Dim fileBuffer As Byte()
    13. fileBuffer = New Byte(100000) {}
    14. Dim fileUpdateCommand As SqlCommand = New SqlCommand("update table set [file].WRITE(@Data, NULL, NULL) where [INDEX] ='" & IntNo & "'", ITA.VAR.Connection)
    15. fileUpdateCommand.Parameters.Add("@Data", SqlDbType.Binary)
    16. Using fileStream As New FileStream(Filepath, FileMode.OpenOrCreate, FileAccess.Read)
    17. totalSize = fileStream.Length
    18. While fileStream.Read(fileBuffer, 0, fileBuffer.Length) > 0
    19. fileUpdateCommand.Parameters("@Data").Value = fileBuffer
    20. fileUpdateCommand.ExecuteNonQuery()
    21. sentBytes += fileBuffer.Length
    22. progressPercentage = (sentBytes / totalSize) * 100
    23. worker.ReportProgress(progressPercentage)
    24. End While
    25. End Using
    26. fileUpdateCommand.Dispose()
    27. SQLConClose()
    28. Return Nothing
    29. End Function
    30. Private Sub _BackgroundWorker_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles _BackgroundWorker.ProgressChanged
    31. Me.Progressbar1.Value = e.ProgressPercentage
    32. End Sub
    33. Private Sub _BackgroundWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles _BackgroundWorker.RunWorkerCompleted
    34. Progressbar1.Value = 0
    35. End Sub



    Hier ist der Code für den Download, hier funktioniert die progressbar nicht und das UI friert ein. weiss jemand warum?

    VB.NET-Quellcode

    1. Private Sub _BackgroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles _BackgroundWorker.DoWork
    2. Dim worker As BackgroundWorker = CType(sender, BackgroundWorker)
    3. worker.WorkerReportsProgress = True
    4. e.Result = RetrieveFile(worker, e)
    5. End Sub
    6. Private Function RetrieveFile(ByVal worker As BackgroundWorker, ByVal e As DoWorkEventArgs) As Byte()
    7. Dim objReader As SqlDataReader
    8. Try
    9. Cmd.Connection = ITA.VAR.Connection
    10. SQLConOpen()
    11. Cmd.CommandText = "SELECT datalength(File) AS fileSize, File FROM table where [INDEX]=''" & IntNo & "'"
    12. objReader = Cmd.ExecuteReader(CommandBehavior.SequentialAccess)
    13. While objReader.Read
    14. If objReader.HasRows Then
    15. Dim memory As New MemoryStream
    16. Dim startIndex As Long = 0
    17. Const ChunkSize As Integer = 256
    18. Dim totalSize As Long = objReader("fileSize")
    19. Dim progressPercentage As Integer
    20. Do
    21. Dim buffer(ChunkSize - 1) As Byte
    22. Dim retrievedBytes As Long = objReader.GetBytes(1, startIndex, buffer, 0, ChunkSize)
    23. memory.Write(buffer, 0, CInt(retrievedBytes))
    24. startIndex += retrievedBytes
    25. progressPercentage = (startIndex / totalSize) * 100
    26. worker.ReportProgress(progressPercentage)
    27. If retrievedBytes <> ChunkSize Then
    28. Exit Do
    29. End If
    30. Loop
    31. SQLConClose()
    32. Dim data() As Byte = memory.ToArray()
    33. memory.Dispose()
    34. Return data
    35. End If
    36. End While
    37. Catch ex As Exception
    38. End Try
    39. End Function


    Jenni Jenni

    VB.NET-Quellcode

    1. ​e.Result = RetrieveFile(worker, e)


    Darum, das Result läuft im MTA Thread (UI Thread) so das der Background Worker effektiv garnichts macht. Dass ist aber nicht absolut sicher sondern nur eine erste Einschätzung von mir. Der Background Worker Invoked ja zwischen seinen eigenen Arbeits Thread und den MTA, so das du alles was die UI Einfrieren lässt im Worker Thread machen musst. Am besten das Byte Array oder was auch immer wo du die Daten Sammelst (Ich würde ein Memory Stream nehmen) zurück geben aus der 2.ten Funktion, dann in eine Variable und diese dann Invoken oder als Result zurück schicken.

    LG, Jenni Herbrich

    r0tzi schrieb:

    update table set [file].WRITE(@Data, NULL, NULL) where [INDEX] ='" & IntNo & "'"
    Dieser Befehl scheint mir das File in mehrere Stücke zu zerteilen, für welcche jeweils ein Datensatz angelegt wird.
    Von der Datenhaltung her ungewöhnlich und unintuitiv - ist eine popelige Progressbar wirklich diesen Aufwand wert?
    Hallo, ich muss den alten Thread nochmal auspacken, weil ein "Problem" aufgetaucht ist.

    Wenn ich eine einzelne Excel Datei zerstückelt hochlade, und dieser zerstückelt wieder runterlade, bringt er mir im Excel eine Fehlermeldung, dass Datei beschädigt ist und ob er versuchen soll soviel wie möglich zu reparieren.
    Hat jemand eine Ahnung woran das liegen könnte?

    Was ich schon probiert habe, ist die Datei als ZIP und/oder als XML hochzuladen und dann wieder umzuwandeln als .xlsx ... Leider kommt der gleiche Fehler :( ...

    Irgendwo gehen da ein paar Byte verloren... komischerweise nur bei Excel Dateien.

    Danke für eure Ideen und Hilfe =)

    r0tzi schrieb:

    Wenn ich eine einzelne Excel Datei zerstückelt hochlade, und dieser zerstückelt wieder runterlade, bringt er mir im Excel eine Fehlermeldung, dass Datei beschädigt ist und ob er versuchen soll soviel wie möglich zu reparieren.
    Hat jemand eine Ahnung woran das liegen könnte?
    Da liegt ein Fehler vor, beim Zerstückeln und Zusammensetzen.

    Schreib dir zuerst eine TestMethode, die eine Test-Datei erst zerstückelt, und dann wieder zusammensetzt.
    Dann prüfen, dass die neu-Zusammengesetzte Datei den Fehler reproduziert.

    Erst danach kannst du dich dranmachen, die beiden Methoden so zu verbessern, dass der Fehler am Ende behoben ist.
    @r0tzi Wenn du die Dateien zerstückelst, wirst du sicherlich mit Offsets arbeiten.

    Ich nehme an, du hast eine Klasse, die sich ausschließlich darum kümmert, Dateien zu zerbrechen und wieder zusammenzukleben.

    In solchen Fällen bieten sich Unit Tests super an, um statisch bekannte Daten auszutesten.
    Aus dem Bauch heraus würde ich mal behaupten, du setzt irgendwo den Offset einen zu weit nach vorne oder nach hinten.
    Quellcode lizensiert unter CC by SA 2.0 (Creative Commons Share-Alike)

    Meine Firma: Procyon Systems
    Meine Privatwebseite: SimonC.eu

    Bitte nicht wundern, wenn meine Aktivitäten im Forum etwas langsamer sind, ich baue gerade mein Nebengewerbe zum Vollgewerbe aus.
    Ich versuche auf euch zurückzukommen :)