Datei aus Multipart parsen und speichern, seltsam codiert?

larsemann

Mitglied
Hallo,

ich habe ein echt seltsames Problem, dass ich einfach nicht gelöst bekomme.

Ein Webservice-API gibt Multipart-Nachrichten zurück und wenn ich den entsprechenden _GET-Aufruf im Browser mache, erhalte ich eine "völlig" andere Datei, als wenn ich den _GET-Aufruf mit Java mache.

Das Framework, was ich benutze ist Spring. Hat jemand von euch schon mal diesen "Effekt" gehabt?

Hier der Ausschnitt, den der Browser liefert:
----------------------------525641684547342045109852
Content-Disposition: form-data; name="name"

bild1.jpg
----------------------------525641684547342045109852
Content-Disposition: form-data; name="bild1"; filename="bild1.jpg"
Content-Type: image/jpeg

ˇÿˇ‡JFIFˇ˛;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 95
ˇ€C   
[..]
----------------------------525641684547342045109852

Das speichert mein Java-Programm:
----------------------------525641684547342045109852
Content-Disposition: form-data; name="name"

bild1.jpg
----------------------------525641684547342045109852
Content-Disposition: form-data; name="bild1"; filename="bild1.jpg"
Content-Type: image/jpeg

ÿØÿàJFIFÿþ;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 95
[..]
----------------------------525641684547342045109852


Dabei geht es mir natürlich auch um das Bild. Ich kann per Java das Bild einfach nicht aus dem Multipart extrahieren, die erzeugte .jpg-Datei ist immer "corrupt".

Das Browser-Ergebnis funktioniert immer...


Vielleicht kann mir hier jemand auf die Sprünge helfen :/
 
Guten Morgen,

Code:
         String url = "localhost:8888/getImage?id=1";
         URL navUrl = new URL(url);
         URLConnection con = (URLConnection)navUrl.openConnection();
         result = getContent(con);
         [..]

Update:

Wenn ich mit einem Rest-Client die Seite aufrufe, zeigt er mir auch immer das zuvor gepostete untere Ergebnis an :/

Update 2: ein Direktaufruf m. Curl erzeugt das korrekte Format wie erwartet
 
Zuletzt bearbeitet:
Das Problem muss in der getContent Methode liegen. Wahrscheinlich interpretiert sie die Antwort als Text. Das kann passieren wenn z.B. ein InputStreamReader anstelle eines InputStream verwendet wird.
 
Code:
			String requestUrl = "http://localhost:8888/getImage?id=1";
			RestTemplate restTemplate = new RestTemplate();


			restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
			HttpEntity<String> entity = new HttpEntity<String>("test");
			ResponseEntity<byte[]> response = restTemplate.exchange(requestUrl, HttpMethod.GET, entity, byte[].class);

			Path path = Paths.get("/tmp/tmp_file");
			Files.write(path, response.getBody());

Jetzt enthält tmp_file wieder den falschen Output .... Langsam dreh ich noch durch.

Das Parsen ist auch eine eher lästige Aufgabe. Gibt es da nichts "Fertiges"?
 
Zuletzt bearbeitet:
Hi,

genau den Ansatz habe ich gewählt!

Hier mein Quelltext:

Code:
		try {


			String requestUrl = "http://localhost:8888/getImage?id=1";
			RestTemplate restTemplate = new RestTemplate();

			restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
			HttpEntity<String> entity = new HttpEntity<String>("test");
			ResponseEntity<byte[]> response = restTemplate.exchange(requestUrl, HttpMethod.GET, entity, byte[].class);

                        // zwischenspeichern ....
			Path path = Paths.get("/tmp/tmp-file");
			Files.write(path, response.getBody());
			Scanner scanner = new Scanner(path);
			byte[] boundary = scanner.nextLine().getBytes();
			System.out.println("boundary: "+ boundary);

			MultipartStream multipartStream = new MultipartStream(new ByteArrayInputStream(response.getBody()), boundary);

			boolean nextPart = multipartStream.skipPreamble();


			while(nextPart) {
				String headers = multipartStream.readHeaders();
				System.out.println("Headers:"+ headers);

				nextPart = multipartStream.readBoundary();
			}

		} catch (Exception e) {
			log.info("exception ", e);
		}

führt zu:

boundary: [45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 55, 50, 57, 51, 48, 49, 53, 52, 53, 51, 54, 54, 55, 54, 52, 51, 53, 49, 48, 49, 57, 49, 55, 54]
Headers: Content-Disposition: form-data; name="my_value"
exception
org.apache.commons.fileupload.MultipartStream$MalformedStreamException: Stream ended unexpectedly
at org.apache.commons.fileupload.MultipartStream.readBoundary(MultipartStream.java:477) ~[commons-fileupload-1.3.1.jar:1.3.1]
at com.neavis.vrac.HomeController.index(Main.java:63) ~[Main.class:na]
[..]

Main.java.:63 ist
nextPart = multipartStream.readBoundary();
 
Ich kann Dir da keine besondere Hilfe sein, die FileUpload-Lib hatten wir mal in einem Projekt.

Ich weiss jetzt auch nicht was die Klasse Scanner in diesem Zusammenhang macht.

Ich würde das zwischenspeichern erst mal weglassen.

Eventuell ist auch das boundary falsch.
 
Hi,

danke trotzdem!

Habe den Quelltext noch etwas optimiert.

Java:
                        // .. bekannt

			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(response.getBody());

			String boundaryExample = "----------------------------495985807863582227169678";



				for(int i = 0; i<boundaryExample.length(); i++) {
					byte b = (byte) byteArrayInputStream.read();


					byteArrayOutputStream.write(b);
				}
			System.out.println("boundary: " + byteArrayOutputStream.toString() );

			byteArrayInputStream.reset();

			MultipartStream multipartStream = new MultipartStream(byteArrayInputStream, byteArrayOutputStream.toByteArray());

			boolean nextPart = multipartStream.skipPreamble();


			while(nextPart) {
				String headers = multipartStream.readHeaders();
				System.out.println("Headers: {}", headers);

				nextPart = multipartStream.readBoundary();
			}

Leider nurnoch die Ausgabe des Boundaries.

Naja, ich mache mal morgen weiter.

Grüße,
 
Zuletzt bearbeitet:
Ich bin mir nicht sicher was du falsch machst, aber das hier funktioniert.

Java:
static byte[] getData(String url) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    InputStream in = new URL(url).openStream();
    int b;
    while((b = in.read()) >= 0) {
        out.write(b);
    }
    in.close();
    return out.toByteArray();
}

static void writeDataToFile(String fileName, byte[] data) throws IOException {
    Files.write(Paths.get(fileName), data);
}

...

byte[] data = getData("http://www.java-forum.org/images/misc/java_forum_org.gif");
writeDataToFile("java_forum.gif", data);
 
Hi, danke erstmal!

Jetzt habe ich den Multipart-Teil selbst geparsed, aber immernoch keinen speicherbaren ByteStream, der mir ein Bilder anzeigt :/

Vielleicht kannst du mir hier auch nochmal auf die Sprünge helfen :/

Code:
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(response.getContent()); 
Scanner sc = new Scanner(byteArrayInputStream);
String boundary = sc.nextLine();
Part path;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int part = 0;
while( sc.hasNextLine()) {
    String nextLine = sc.nextLine();
    if ( nextLine.contains(boundary) ) {
    switch(part) {
	case 0:
	     path = Paths.get("/tmp/part_0.json");
        break;
	default :
	     path = Paths.get("/tmp/part_"+part+".jpg");
	break;
     }
	Files.write(path, byteArrayOutputStream.toByteArray());

	byteArrayOutputStream.reset();
	part = part+1;

     } else {					
	byteArrayOutputStream.write(nextLine.getBytes());
    }
}
 
Zuletzt bearbeitet:
Du könntest deine eigene InputStream Klasse definieren.

Java:
class ByteArray {
    byte[] array = new byte[10];
    int size = 0;
    
    void grow() {
        array = Arrays.copyOf(array, array.length*3/2);
    }
    
    void add(byte b) {
        if(size >= array.length) grow();
        array[size++] = b;
    }
    
    void removeLast(int n) {
        size -= n;
    }
    
    byte[] toArray() {
        return Arrays.copyOf(array, size);
    }
}

class DataInputStream {
    final InputStream in;
    
    DataInputStream(InputStream in) {
        this.in = in;
    }
    
    int read() throws IOException {
        return in.read();
    }
    
    byte[] readBytesUntil(String str) throws IOException {
        ByteArray bytes = new ByteArray();
        int matching = 0;
        while(true) {
            int b = in.read();
            if(b < 0) break;
            bytes.add((byte)b);
            if(str.charAt(matching) == (char)b) matching++;
            else matching = 0;
            if(matching == str.length()) {
                bytes.removeLast(str.length());
                break;
            }
        }
        return bytes.toArray();
    }
    
    String readLine() throws IOException {
        StringBuilder builder = new StringBuilder();
        while(true) {
            int b = in.read();
            if(b < 0 || b == '\n') break;
            if(b != '\r') builder.append((char)b);
        }
        return builder.toString();
    }
}

...
byte[] bytes = "abcdef01234".getBytes();    
DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));
System.out.println(new String(in.readBytesUntil("23")));
 
Danke, dass hat mir echt weitergeholfen.

Ich habe es jetzt etwas klassischer gelöst, glaube ich. Allerdings noch nicht ganz.
Das Problem ist eigentlich, dass meine Quelle kein standardkonformes Multipart erzeugt.
Code:
ByteArrayOutputStream current = new ByteArrayOutputStream();
ByteArrayOutputStream partContent = new ByteArrayOutputStream();
            int part = 0;
String boundary = null;
 
int b;
int prevB = 0;
while( byteArrayInputStream.available() > 0 ) { // byteArrayInputStream 
    current.reset();
    while( (b = byteArrayInputStream.read()) > 0 ) {
 
        if ( b == 10 && prevB == 13 ) { // check if b == LF and prevB == CR
            current.write(10);
            break; // break loop
        }
        else current.write(b);
 
        prevB = b;
    }
 
    if ( boundary == null) { // first line is boundary
        boundary = current.toString();
        boundary = boundary.substring(0, boundary.length()-2); // remove carriage return
    } else {
        if ( current.toString().contains(boundary) ) {
            switch (part) {
                case 0:
                    Files.write(Paths.get("/tmp/json.bin"), partContent.toByteArray()); // first part is a json value
                    break;
                default:
                    Files.write(Paths.get("/tmp/" + part+".bin"), partContent.toByteArray()); // write binary file content
                    break;
                }
 
            partContent.reset();
            part++;
        } else {
            partContent.write(current.toByteArray());
        }
 
    }
}

Das erzeugt ungefähr das, was ich benötige. Allerdings gibt es jetzt einen feinen Unterschied zwischen Quelle und meinem geparsten Ergebnis.
Ich weiss einfach nicht, wieso im Ergebnis alle "^@" verschwunden sind :/
 

Anhänge

  • Screen Shot 2014-02-23 at 20.33.10.jpg
    Screen Shot 2014-02-23 at 20.33.10.jpg
    147,5 KB · Aufrufe: 23

Neue Themen


Zurück
Oben