All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.davidmoten.oa3.codegen.http.Multipart Maven / Gradle / Ivy

There is a newer version: 0.1.22
Show newest version
package org.davidmoten.oa3.codegen.http;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;

public final class Multipart {
    
    private Multipart() {
        // prevent instantiation
    }

    public static String randomBoundary() {
        return nextId();
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {

        private final List formEntries = new ArrayList<>();

        public Builder addFormEntry(String key, byte[] value, Optional filename, Optional contentType) {
            formEntries.add(new FormEntry(key, value, filename, contentType));
            return this;
        }

        public Builder addFormEntry(String key, String value, Optional filename, Optional contentType) {
            return addFormEntry(key, value.getBytes(StandardCharsets.UTF_8), filename, contentType);
        }
        
        /**
         * Includes leading CRLF for http content.
         * 
         * @return multipart content bytes
         */
        public byte[] multipartContent(String boundary) {
            try (BytesOutputStream w = new BytesOutputStream()) {
                for (FormEntry entry : formEntries) {
                    w.write("--");
                    w.write(boundary);
                    w.eol();
                    String fname = entry.filename.map(x -> "; filename=\"" + x + "\"").orElse("");
                    w.write("Content-Disposition: form-data; name=\"" + entry.key + "\"" + fname);
                    w.eol();
                    if (entry.contentType.isPresent()) {
                        w.write("Content-Type: " + entry.contentType.get());
                        w.eol();
                    }
                    w.eol();
                    w.write(entry.value);
                    w.eol();
                }
                w.write("--");
                w.write(boundary);
                w.write("--");
                w.flush();
                return w.toByteArray();
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }
        }

    }

    private static final class FormEntry {
        final String key;
        final byte[] value;
        final Optional filename;
        final Optional contentType;

        FormEntry(String key, byte[] value, Optional filename, Optional contentType) {
            this.key = key;
            this.value = value;
            this.filename = filename;
            this.contentType = contentType;
        }
    }

    private static final class BytesOutputStream extends ByteArrayOutputStream {

        void write(String s) throws IOException {
            write(s.getBytes(StandardCharsets.UTF_8));
        }

        void eol() throws IOException {
            write("\r\n");
        }
    }

    private static final Random RANDOM = new Random();
    private static final String ID_CHARS = "abcdefghijkmnopqrstuvwxyz23456789"; // remove 1, l, 0 because hard to
                                                                                // distinguish
    private static final int ID_LENGTH = 16;

    /**
     * Returns a new random id that is guaranteed unique given the expected number
     * of calls for use multipart boundaries.
     *
     * 

* Number of id permutations is ID_CHARS.length()ID_LENGTH. * *

* Probability of collision after k calls to this method and given n * permutations is approximately 1 - e-k2/2n. * *

* For k = 1 million, 33 characters, id length 16, probability of collision is 2 * x 10-13. That is one in 20 million million chance. * * @return next unique identifier (for egc broadcast id ) */ private static String nextId() { char[] a = new char[ID_LENGTH]; for (int i = 0; i < a.length; i++) { a[i] = ID_CHARS.charAt(RANDOM.nextInt(Integer.MAX_VALUE) % ID_CHARS.length()); } return new String(a); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy