Image Upload dauert ewig lange

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Chris schrieb:

    Abgesehen von Claus Einwand fällt mir noch einiges ein.

    Ist der Server unter deiner Kontrolle oder hast du Zugriff darauf? Der kann auch lahm sein.
    Networkdebugging einschalten.
    developer.apple.com/documentat…etwork_diagnostic_logging
    mit nscurl --ats-diagnostics prüfen was der Server für SSL Versionen kann.
    Ich hab muss bei einem Server in unserer Software TLS 1.3 abschalten weil der sonst nur Fehlerhaft antwortet.
    Ich vermisse den Content-Type. der ist application/jason
    Instruments kannst du auch einsetzen.
    Ich befinde mich aktuell in den USA und hatte auch schon überlegt ob vielleicht auch die Entfernung zu meinem kleinen Server ein Problem darstellen könnte ? Arbeite mit einem Freund der den Server verwaltet zusammen. Werde ihm das mal mitteilen und mich dann nochmal bezüglich des Servers hier melden.
  • ThisIsBeat schrieb:


    PS: Ich verstehe nebenbei nicht warum manche von euch behaupten das JSON dafür nicht geeignet sei. Sämtliche Quellen die ich mir angeschaut habe sagen das encodete Images sehr wohl über den Body versendet werden können. Zumal ich die Bilder bereits in binary Data konvertiere (developer.apple.com/documentat…/uiimage/1624115-jpegdata). Eure Quellen in Form von Links oder auch gerne Beispiele würde ich gerne sehen. Will ja was dazu lernen.
    Ich tippe jetzt mal, dass die Bilder im JSON Format Base64 encoded werden, was dazu führt, dass die JSON Daten viel Größer als die reinen Daten der Bilder werden.

    Daher würde es sich anbieten die Bilder direkte als image/jpeg zu senden, da damit weniger Daten übertragen werden müssen und der Upload somit schneller sein sollte. ;)
  • MCDan schrieb:

    Ich tippe jetzt mal, dass die Bilder im JSON Format Base64 encoded werden, was dazu führt, dass die JSON Daten viel Größer als die reinen Daten der Bilder werden.
    Daher würde es sich anbieten die Bilder direkte als image/jpeg zu senden, da damit weniger Daten übertragen werden müssen und der Upload somit schneller sein sollte. ;)
    Ob die jetzt in Base64 encoded werden kann ich ich jetzt so gar nicht sagen aber okay, sagen wir mal das stimmt und dadurch werden mehr Daten als nötig versendet. Mein Plan ist es ja, mehrere Bilder die sich in einem Array befinden gleichzeitig zu versenden.
    Nur ist das mit dem versenden eines [Data] Arrays so eine Sache.

    Aktuell schaut mein Code so aus:

    Quellcode

    1. guard let uploadData = try? JSONEncoder().encode(jpegImages) else {return}
    2. let imageSize: Int = uploadData.count
    3. print("actual size of images in KB: %f ", Double(imageSize) / 1000.0)
    4. var request = URLRequest(url: url)
    5. request.httpMethod = "POST"
    6. let configuration = URLSessionConfiguration.default
    7. let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
    8. let task = session.uploadTask(with: request, from: uploadData)
    9. task.resume()
    Alles anzeigen
    In dem Array jpegImages befinden sich die JPEG Bilder. Nun kann ich dieses [Data] Array nicht im ganzen ohne weiteres verschicken, weil Data erwartet wird. Deswegen ja das encoden, denn dann wird es möglich.

    Jetzt die Frage: Wie bekomme ich es hin alle Bilder gleichzeitig hochzuladen ohne zu encoden ?
  • ThisIsBeat schrieb:




    Jetzt die Frage: Wie bekomme ich es hin alle Bilder gleichzeitig hochzuladen ohne zu encoden ?
    Da fällt mir spontan Multipart/Mime ein.
    Damit hatte ich im letzten Jahrtausend mal Berührung. Metadaten als Text und Messdaten als Binary.
    Ich hab aber keine Ahnung ob da was am Server gemacht werden muss.
    Man macht einfach solange irgendwelche Dinge, bis man tot ist.
    Und dann bekommen die anderen Kuchen.
  • ThisIsBeat schrieb:

    Einziger Haken ist das ich die compressionQuality senken muss. Die Qualität ist wie gesagt so das ich damit leben kann aber man will ja immer das Optimum rausholen.
    Wenn ich also jetzt noch die compressionQuality erhöhen könnte ohne weitere Einbußen zu haben wäre das super !
    Schon ein bisschen witzig: Warum meinst du erzählt dir hier jeder, dass jpg nicht in JSON gehört? ;)
    Du willst das Optimum, aber bläst deine Daten um mehr als ein Drittel auf (ganz konkret: Du könntest eigentlich vier Bilder versenden, in der Zeit, die du für drei brauchst)

    ThisIsBeat schrieb:

    PS: Ich verstehe nebenbei nicht warum manche von euch behaupten das JSON dafür nicht geeignet sei. Sämtliche Quellen die ich mir angeschaut habe sagen das encodete Images sehr wohl über den Body versendet werden können.
    Gehen tut viel: Du könntest die Bilder aber auch ausdrucken, per Post verschicken, und dann wieder einscannen ;) - nur ob das effektiv ist, steht auf einem anderem Blatt.
    en.m.wikipedia.org/wiki/Base64
  • Hallo ThisIsBeat!

    ThisIsBeat schrieb:

    [...]

    Jetzt aber wieder zurück zum Thema. Also ich verwende aktuell eine Funktion die meine Bilder resized. Wie gesagt dadurch habe ich die Upload Menge auf 900Kb reduziert und der Upload geht viel schneller über den Tisch. Einziger Haken ist das ich die compressionQuality senken muss. Die Qualität ist wie gesagt so das ich damit leben kann aber man will ja immer das Optimum rausholen.
    Wenn ich also jetzt noch die compressionQuality erhöhen könnte ohne weitere Einbußen zu haben wäre das super !

    [...]
    PS: Ich verstehe nebenbei nicht warum manche von euch behaupten das JSON dafür nicht geeignet sei. Sämtliche Quellen die ich mir angeschaut habe sagen das encodete Images sehr wohl über den Body versendet werden können. Zumal ich die Bilder bereits in binary Data konvertiere (developer.apple.com/documentat…/uiimage/1624115-jpegdata). Eure Quellen in Form von Links oder auch gerne Beispiele würde ich gerne sehen. Will ja was dazu lernen.
    Ich habe den Eindruck, dass es bisher nicht deutlich genug geschrieben wurde: deine 900KB stimmen nicht! Es sind eher 24MB entsprechend deiner Aussage oben:

    Größe von uploadData ist nach dem Encoding: 24134.544KB

    D.h. mit JSON überträgst du etwa die 27-fache Menge an Daten. Binär könntest du in derselben Zeit also 27 Bilder übertragen. Das ist der Grund, warum JSON hierfür nicht passend ist. Wenn es technisch geht, die Daten binär zu übertragen, solltest du das darauf umstellen.

    Trotz alldem dauert es nachdem ich auf den Upload Button geklickt habe rund 11 Sekunden bis ich die Antwort vom Server bekomme.

    Mal rechnen: 24134,544*8/11=17552,39... kbit/s Upload. Scheint mir nicht so schlecht. Hatte ich bis vor einem halben Jahr nicht einmal im Download.
  • @marcoo so jetzt extra nochmal für dich, denn ich hab den Eindruck das du nicht jeden einzelnen Post hier ließt.
    Ich habe gesagt, dass ich eine Funktion namens resize (Post 20) verwende um die Größe von uploadData runterzuschrauben auf die besagten 900Kb. So und jetzt kommst du...

    Dann zu den binären Daten. Ebenfalls in der Funktion resize wirst du folgende Zeile finden, sowie in Post Nr. 1:

    Quellcode

    1. let imageData = img?.jpegData(compressionQuality: CGFloat(compressionQuality))
    mit dieser Zeile konvertiere ich die Bilder in JPEG Data um und füge sie anschließend einem Array hinzu.
    Mein Problem ist nicht eines dieses Bilder zu versenden, denn dann würde ich es einfach wie in diesem Beispiel machen (stackoverflow.com/questions/23…-to-server-as-binary-data). Ich muss das ganze Array auf einmal hochladen. Da JSON aber Data erwartet ich aber ein [Data] Array habe, Encode ich das ganze.

    Deswegen habe ich eine ganz konkrete Frage gestellt:
    Wie kann ich das [Data] Array versenden ohne es zu encoden ?

    So und wenn du der Meinung bist das sich JSON sich dafür nicht eignet, wäre ich die sehr verbunden wenn du einen Link oder Beispiel posten könntest in dem eine alternative zu JSON zu sehen ist.
  • t-no schrieb:

    Schon ein bisschen witzig: Warum meinst du erzählt dir hier jeder, dass jpg nicht in JSON gehört? ;-)Du willst das Optimum, aber bläst deine Daten um mehr als ein Drittel auf (ganz konkret: Du könntest eigentlich vier Bilder versenden, in der Zeit, die du für drei brauchst)
    Was ich halt witzig finde ist das sämtliche Seiten im Netz JSON verwenden. Wie gesagt ich wäre für ein Beispiel sehr sehr dankbar.

    Habe dann aber mal folgendes ausprobiert:


    Quellcode

    1. DispatchQueue.main.async {
    2. guard let convertImage = image.key!.jpegData(compressionQuality: 1.0) else {return}
    3. let test = convertImage.base64EncodedData()
    4. let imageSize: Int = convertImage.count
    5. print("actual size of image in KB: %f ", Double(imageSize) / 1000.0)
    6. self.jpegImages.append(test)
    7. }
    Ergebnis war das uploadData rund 2 Mb größer war als wenn ich mir die variable test einfach spare...
  • MCDan schrieb:

    Du könntest die Bilder alternativ noch als ZIP Archiv packen und dann das komplette ZIP Archiv als Binary hochladen. Der Server müsste dann natürlich angepasst werden und das ZIP Archiv entpacken und die darin enthaltenen Bilder verarbeiten.
    Aber verwende ich nicht schon ein Binary Format mit dieser Funktion (developer.apple.com/documentat…/uiimage/1624115-jpegdata). Selbst wenn ich es wie in Post Nr. 29 auf Base64 umstelle wird das ganze nur größer ...
  • Hier nochmal der aktuelle Stand der Dinge wie es in meinem Code ausschaut:

    viewDidLoad:

    Quellcode

    1. DispatchQueue.main.async {
    2. let resizedImage = self.resize(image.key!)
    3. guard let convertImage = resizedImage.jpegData(compressionQuality: 0.65) else {return}
    4. let imageSize: Int = convertImage.count
    5. print("actual size of image in KB: %f ", Double(imageSize) / 1000.0)
    6. self.jpegImages.append(convertImage)
    7. }



    Image resize:

    Quellcode

    1. func resize(_ image: UIImage) -> UIImage {
    2. var actualHeight = Float(image.size.height)
    3. var actualWidth = Float(image.size.width)
    4. let maxHeight: Float = 1080.0
    5. let maxWidth: Float = 1080.0
    6. var imgRatio: Float = actualWidth / actualHeight
    7. let maxRatio: Float = maxWidth / maxHeight
    8. let compressionQuality: Float = 0.65
    9. //50 percent compression
    10. if actualHeight > maxHeight || actualWidth > maxWidth {
    11. if imgRatio < maxRatio {
    12. //adjust width according to maxHeight
    13. imgRatio = maxHeight / actualHeight
    14. actualWidth = imgRatio * actualWidth
    15. actualHeight = maxHeight
    16. }
    17. else if imgRatio > maxRatio {
    18. //adjust height according to maxWidth
    19. imgRatio = maxWidth / actualWidth
    20. actualHeight = imgRatio * actualHeight
    21. actualWidth = maxWidth
    22. }
    23. else {
    24. actualHeight = maxHeight
    25. actualWidth = maxWidth
    26. }
    27. }
    28. let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
    29. UIGraphicsBeginImageContext(rect.size)
    30. image.draw(in: rect)
    31. let img = UIGraphicsGetImageFromCurrentImageContext()
    32. let imageData = img?.jpegData(compressionQuality: CGFloat(compressionQuality))
    33. UIGraphicsEndImageContext()
    34. return UIImage(data: imageData!) ?? UIImage()
    35. }
    Alles anzeigen

    Upload:

    Quellcode

    1. guard let uploadData = try? JSONEncoder().encode(jpegImages) else {return}
    2. let imageSize: Int = uploadData.count
    3. print("actual size of images in KB: %f ", Double(imageSize) / 1000.0) // 900Kb
    4. var request = URLRequest(url: url)
    5. request.httpMethod = "POST"
    6. let configuration = URLSessionConfiguration.default
    7. let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
    8. let task = session.uploadTask(with: request, from: uploadData)
    9. task.resume()
    Alles anzeigen

    EDIT:
    Habe jetzt nochmal folgendes ausprobiert mit base64EncodedString, aber das dauert ewig bis das ganze in der Konsole fertig wird:

    Quellcode

    1. DispatchQueue.main.async {
    2. let resizedImage = self.resize(image.key!)
    3. guard let convertImage = resizedImage.jpegData(compressionQuality: 0.65) else {return}
    4. let test = convertImage.base64EncodedString(options: .lineLength64Characters)
    5. print(test)
    6. let imageSize: Int = convertImage.count
    7. print("actual size of image in KB: %f ", Double(imageSize) / 1000.0)
    8. self.jpegImages.append(convertImage)
    9. }

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von ThisIsBeat ()

  • Ok ich versuche es mal in der langen Version weil du anscheinend total auf dem Schlauch stehst oder einfach keinen Bock hast. Dich zu informieren was du da eigentlich machst.

    Ein Bild (Image) besteht in der Regel aus Pixeln. Bei einem 24bit Image (heutiger Standard) hat dabei jedes Pixel 3 Byte. Sagen wir also mal du hast ein 10 Mega Pixel Image, dann hast du 10*3 = 30 Megabyte Daten ohne jede Kompression. Mit der jpeg Kompression werden nun diese Bilder in einer sagen wir mal 80% Qualität auf 1 Megabyte runterkomprimiert. Das bedeutet dein Bild hat jetzt zwar nur noch 1/10 der Datenmenge, dafür aber auch nur noch 80% der Qualität die es vorher hatte. Was du nun machst ist, du machst das Bild noch schlechter indem du die Qualität oder sogar die Anzahl Pixel noch weiter runterschraubst. Dadurch kannst du aus den 1 Megabyte dann vielleicht 800 Kilobyte machen. Toll...

    Egal was auch immer. Diese Bilddateien stehen in der Datei dann einfach hintereinander weg. Also Byte für Byte. Wobei ein Byte eben den wertebereich 0-255 hat. Textformat haben einen anderen Wertebereich von 0-127 (jaja ich weiß das ist jetzt stark vereinfacht aber noch weiter mag ich das jetzt wirklich nicht ausführen)

    Du gehst jetzt daher und encodest diese Daten mit einem json encoder. Json ist aber eben kein binary Format sondern ein Textformat. Das bedeutet die Bytes 0-255 deines Bildes Müssen so umkodiert werden, dass sie in einen Wertebereich von 0-127 reinpassen. Das bedeutet wenn du eine binäre Datei in ein Textformat encodierst wird sie viel größer werden.

    Was du also im Moment machst ist, du verschlechterst deine Bilder um die Upload Größe zu verkleinern, statt die Upload Methode zu ändern die der eigentlich Grund dafür ist das du viel mehr Daten versendest als eigentlich notwendig...

    Manchmal sollte man sich einfach mal mit den Grundlagen beschäftigen und verstehen was warum wie funktioniert,bevor man hergeht und einfach nur irgendwelchen code zusammenkopiert den man nicht versteht.

    Claud
    2 Stunden Try & Error erspart 10 Minuten Handbuchlesen.

    Pre-Kaffee-Posts sind mit Vorsicht zu geniessen :)

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von Thallius ()

  • ThisIsBeat schrieb:

    MCDan schrieb:

    Du könntest die Bilder alternativ noch als ZIP Archiv packen und dann das komplette ZIP Archiv als Binary hochladen. Der Server müsste dann natürlich angepasst werden und das ZIP Archiv entpacken und die darin enthaltenen Bilder verarbeiten.
    Aber verwende ich nicht schon ein Binary Format mit dieser Funktion (developer.apple.com/documentat…/uiimage/1624115-jpegdata). Selbst wenn ich es wie in Post Nr. 29 auf Base64 umstelle wird das ganze nur größer ...
    Damit wandelst Du zwar das UIImage in ein NSData Objekt mit JPEG Daten um, allerdings verwendest Du das NSData Objekt dann in einem JSON. Da JSON jedoch kein Binary, sondern nur Text kann, wird der JSONEncoder das NSData Objekt sicherlich in Base64 encoded Text umwandeln. Der Base64 encoded Text ist dann natürlich ca. 33–36% größer als das Binary. Ein 1MB großes NSData Objekt wird somit ca. 1,33-1,36MB groß. Bei 10MB wären es schon ca. 13MB. Bei 3 Bildern a 1MB ergibt dies dann ca. 4MB, 3 x 10MB wird zu ca. 40MB usw.

    Um den "Overhead" von ca. 33–36% durch das Base64 Encoding zu sparen kannst Du die Daten nur als Binary versenden. Da es mehr als 1 Datei ist, wäre evtl. ZIP ein möglicher "Container". Ich verwende hier ausdrücklich die Bezeichnung "Container", da evtl. Einsparungen durch die ZIP Kompression bei JPEG nicht wirklich geben sind. Evtl. bringt es bei Verwendung von ZIP sogar mehr die Bilder als PNG zu encoden. Dies müsstest Du einfach mal testen und die Ergebnisse bei gleichen Ausgangsbildern vergleichen.
  • Thallius schrieb:

    Du gehst jetzt daher und encodest diese Daten mit einem json encoder. Json ist aber eben kein binary Format sondern ein Textformat. Das bedeutet die Bytes 0-255 deines Bildes Müssen so umkodiert werden, dass sie in einen Wertebereich von 0-127 reinpassen. Das bedeutet wenn du eine binäre Datei in ein Textformat encodierst wird sie viel größer werden
    Vielen Dank für die ausführliche Erklärung, hat mir jetzt schon weitergeholfen ! Ich meine im Endeffekt weiß ich ja was ich da mache und das darunter die Qualität des Bildes leidet. Aber gut, dann verabschiede ich mich mal von JSON. Ich habe jetzt auch weiter recherchiert und bin da auf Multiform-requests gestoßen, was ganz danach aussieht als wäre es die bessere Lösung.

    Und ich stehe tatsächlich auf dem Schlauch und bin das Gegenteil von Faul. Es ist nur so das ich mit Uploads bisher keine Erfahrungen gemacht habe und JSON aus dem Grund verwendet habe, da ich es zum einen nicht besser wusste und zum anderen tatsächlich Methoden im Netz gesehen habe die über JSON funktioniert haben.
    Den Nachteil habe ich ja zu sehen bekommen, was letztendlich aber wieder eine weitere Erfahrung ist die machen konnte.

    Detaillierte Erklärungen wie deine jetzt zum Beispiel sind es dann aber die mir dann weiter helfen :D

    Ich werde jetzt mal mein Glück mit einem Multiform Request probieren und das ganze dann sobald ich fertig bin hier posten...
  • MCDan schrieb:

    Da es mehr als 1 Datei ist, wäre evtl. ZIP ein möglicher "Container". Ich verwende hier ausdrücklich die Bezeichnung "Container", da evtl. Einsparungen durch die ZIP Kompression bei JPEG nicht wirklich geben sind. Evtl. bringt es bei Verwendung von ZIP sogar mehr die Bilder als PNG zu encoden. Dies müsstest Du einfach mal testen und die Ergebnisse bei gleichen Ausgangsbildern vergleichen.
    Ich werde mal sehen wie das ganze jetzt bei einem Multiform-Request ausschauen wird und ob ich einen weg herum um Zip Dateien finde, aber ansonsten definitiv eine Möglichkeit !
  • Alles in eine Zip-Datei zu packen ist unnötig. JPEG wird nicht mehr viel kleiner. Das unnötige komprimieren auf Client- und Server-Seite kann man getrost. Einfach in ein Archive (tar) packen.

    Ich würde sowieso die Bilder einzeln übertragen. Wenn das schief geht, dass soll ja übers Netzwerk übertragen werden, dann muss man nur das eine Bild erneut übertragen und nicht das ganze Archive. Für den Nutzer könnte das auch einen besseren Eindruck erwecken, wenn viele Nutzer den Server auslasten...
  • manoh schrieb:

    Alles in eine Zip-Datei zu packen ist unnötig. JPEG wird nicht mehr viel kleiner. Das unnötige komprimieren auf Client- und Server-Seite kann man getrost. Einfach in ein Archive (tar) packen.

    Ich schrieb ja auch ausdrücklich ZIP als Container und nicht als Packer zu verwenden. ;)

    Mir war jetzt nicht bekannt, dass es auch Frameworks etc. für iOS gibt, mit denen man tar Archive erstellen kann.
  • Also ich habe mir jetzt mal die Zeit genommen und mich mit Multipart Requests auseinander gesetzt (danke @Chris) um auch alles zu verstehen. Ich glaube auch mit Erfolg. Zwar kann ich nicht unbedingt einen viel schnelleren Upload feststellen aber seht selbst:

    Mich würde eure Meinung zu dem ganzen interessieren, kann ich das so machen ? Ich kann sagen das die Qualität jetzt definitiv sehr gut aussieht und die Upload Geschwindigkeit auch passt.

    Quellcode

    1. struct Media {
    2. let key: String
    3. let fileName: String
    4. let data: Data
    5. let mimeType: String
    6. init?(withImage image: UIImage, forKey key: String) {
    7. self.key = key
    8. self.mimeType = "image/jpeg"
    9. self.fileName = "photo\(arc4random()).jpeg"
    10. guard let data = image.jpegData(compressionQuality: 0.7) else {return nil}
    11. self.data = data
    12. }
    13. }
    Alles anzeigen

    Quellcode

    1. let url = URL(string: "https://api.imgur.com/3/image")!
    2. let boundary = generateBoundary()
    3. let dataBody = createDataBody(media: mediaContent, boundary: boundary)
    4. var request = URLRequest(url: url)
    5. request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
    6. request.addValue("Client-ID 65652160bc46280", forHTTPHeaderField: "Authorization")
    7. request.httpMethod = "POST"
    8. request.httpBody = dataBody
    9. let configuration = URLSessionConfiguration.default
    10. let session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
    11. let task = session.dataTask(with: request) {(data, response, error) in
    12. if let response = response {
    13. print(response)
    14. }
    15. if let data = data {
    16. do {
    17. let json = try JSONSerialization.jsonObject(with: data, options: [])
    18. print(json)
    19. } catch {
    20. print(error)
    21. }
    22. }
    23. }
    24. task.resume()
    Alles anzeigen


    Quellcode

    1. func generateBoundary() -> String {
    2. return "Boundary-\(NSUUID().uuidString)"
    3. }
    4. func createDataBody(media: [Media]?, boundary: String) -> Data {
    5. let lineBreak = "\r\n"
    6. var body = Data()
    7. if let media = media {
    8. for item in media {
    9. body.append("--\(boundary + lineBreak)")
    10. body.append("Content-Disposition: form-data; name=\"\(item.key)\"; filename=\"\(item.fileName)\"\(lineBreak)")
    11. body.append("Content-Type: \(item.mimeType + lineBreak + lineBreak)")
    12. body.append(item.data)
    13. body.append(lineBreak)
    14. }
    15. }
    16. body.append("--\(boundary)--\(lineBreak)")
    17. return body
    18. }
    Alles anzeigen