@Override
    protected Output createOutput() throws IOException {
        try {
            final Path tempFile = s3AppenderTempDir.createTempFile();
            LOGGER.trace("Using temp file {}", tempFile);

            // Get a writer for the new lock file.
            final Output output = outputFactory
                    .create(new BufferedOutputStream(Files.newOutputStream(tempFile)));

            return new OutputProxy(output) {
                @Override
                public void close() throws IOException {
                    super.close();

                    try {
                        final S3Manager s3Manager = new S3Manager(pathCreator, s3ClientConfig);
                        final String bucketNamePattern = NullSafe
                                .nonBlank(S3Appender.this.bucketNamePattern)
                                .orElse(s3Manager.getBucketNamePattern());
                        final String keyNamePattern = NullSafe
                                .nonBlank(S3Appender.this.keyNamePattern)
                                .orElse(s3Manager.getKeyNamePattern());

                        // Upload to S3
                        // Upload the zip to S3.
                        final Meta meta = metaHolder.getMeta();
                        final AttributeMap attributeMap = metaDataHolder.getMetaData();

                        s3Manager.upload(bucketNamePattern, keyNamePattern, meta, attributeMap, tempFile);
                    } catch (final RuntimeException e) {
                        fatal(e.getMessage(), e);
                    } finally {
                        Files.deleteIfExists(tempFile);
                    }
                }
            };

        } catch (final RuntimeException e) {
            error(e.getMessage(), e);
            throw new IOException(e.getMessage(), e);
        }
    }
Joins text instances into a single text instance.
stroom-aws/stroom-aws-s3-impl/src/main/java/stroom/aws/s3/impl/S3Appender.java