private void writeSegmentsReplacingExif(final OutputStream outputStream, final List<JFIFPiece> segments, final byte[] newBytes)
            throws ImagingException, IOException {

        try (DataOutputStream os = new DataOutputStream(outputStream)) {
            JpegConstants.SOI.writeTo(os);

            boolean hasExif = false;

            for (final JFIFPiece piece : segments) {
                if (piece instanceof JFIFPieceSegmentExif) {
                    hasExif = true;
                    break;
                }
            }

            if (!hasExif && newBytes != null) {
                final byte[] markerBytes = ByteConversions.toBytes((short) JpegConstants.JPEG_APP1_MARKER, getByteOrder());
                if (newBytes.length > 0xffff) {
                    throw new ImagingOverflowException("APP1 Segment is too long: " + newBytes.length);
                }
                final int markerLength = newBytes.length + 2;
                final byte[] markerLengthBytes = ByteConversions.toBytes((short) markerLength, getByteOrder());

                int index = 0;
                final JFIFPieceSegment firstSegment = (JFIFPieceSegment) segments.get(index);
                if (firstSegment.marker == JpegConstants.JFIF_MARKER) {
                    index = 1;
                }
                segments.add(index, new JFIFPieceSegmentExif(JpegConstants.JPEG_APP1_MARKER, markerBytes, markerLengthBytes, newBytes));
            }

            boolean APP1Written = false;

            for (final JFIFPiece piece : segments) {
                if (piece instanceof JFIFPieceSegmentExif) {
                    // only replace first APP1 segment; skips others.
                    if (APP1Written) {
                        continue;
                    }
                    APP1Written = true;

                    if (newBytes == null) {
                        continue;
                    }

                    final byte[] markerBytes = ByteConversions.toBytes((short) JpegConstants.JPEG_APP1_MARKER, getByteOrder());
                    if (newBytes.length > 0xffff) {
                        throw new ImagingOverflowException("APP1 Segment is too long: " + newBytes.length);
                    }
                    final int markerLength = newBytes.length + 2;
                    final byte[] markerLengthBytes = ByteConversions.toBytes((short) markerLength, getByteOrder());

                    os.write(markerBytes);
                    os.write(markerLengthBytes);
                    os.write(newBytes);
                } else {
                    piece.write(os);
                }
            }
        }
    }
Reads a JPEG image, replaces the EXIF metadata and writes the result to a stream.

<p>
Note that this uses the "Lossy" approach - the algorithm overwrites the entire EXIF segment, ignoring the possibility that it may be discarding data it
couldn't parse (such as Maker Notes).
</p>

@param src       InputStream containing JPEG image data.
@param os        OutputStream to write the image to.
@param outputSet TiffOutputSet containing the EXIF data to write.
@throws ImagingException if it fails to read the JFIF segments
@throws IOException      if it fails to read the image data
@throws ImagingException if it fails to write the updated data
src/main/java/org/apache/commons/imaging/formats/jpeg/exif/ExifRewriter.java