@Override
    public HttpResponseOutputStream<Node> write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
        try {
            final CreateFileUploadRequest createFileUploadRequest = new CreateFileUploadRequest()
                    .directS3Upload(true)
                    .timestampModification(status.getModified() != null ? new DateTime(status.getModified()) : null)
                    .timestampCreation(status.getCreated() != null ? new DateTime(status.getCreated()) : null)
                    .parentId(Long.parseLong(nodeid.getVersionId(file.getParent())))
                    .name(file.getName());
            final CreateFileUploadResponse createFileUploadResponse = new NodesApi(session.getClient())
                    .createFileUploadChannel(createFileUploadRequest, StringUtils.EMPTY);
            if(log.isDebugEnabled()) {
                log.debug(String.format("upload started for %s with response %s", file, createFileUploadResponse));
            }
            final MultipartOutputStream proxy = new MultipartOutputStream(createFileUploadResponse, file, status);
            return new HttpResponseOutputStream<Node>(new MemorySegementingOutputStream(proxy, partsize),
                    new SDSAttributesAdapter(session), status) {
                @Override
                public Node getStatus() {
                    return proxy.getResult();
                }
            };
        }
        catch(ApiException e) {
            throw new SDSExceptionMappingService(nodeid).map("Upload {0} failed", e, file);
        }

    }
@Test
    public void testWriteEncrypted() throws Exception {
        final SDSNodeIdProvider nodeid = new SDSNodeIdProvider(session);
        final Path room = new SDSDirectoryFeature(session, nodeid).createRoom(
            new Path(new AlphanumericRandomStringService().random(), EnumSet.of(Path.Type.directory, Path.Type.volume)), true);
        final byte[] content = RandomUtils.nextBytes(new HostPreferences(session.getHost()).getInteger("sds.upload.multipart.chunksize") + 1);
        final Path test = new Path(room, new NFDNormalizer().normalize(String.format("ä%s", new AlphanumericRandomStringService().random())).toString(), EnumSet.of(Path.Type.file));
        {
            final TripleCryptWriteFeature writer = new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3MultipartWriteFeature(session, nodeid));
            final TransferStatus status = new TransferStatus();
            status.setLength(content.length);
            status.setChecksum(new MD5ChecksumCompute().compute(new ByteArrayInputStream(content), new TransferStatus()));
            status.setModified(1632127025217L);
            final StatusOutputStream<Node> out = writer.write(test, status, new DisabledConnectionCallback());
            assertNotNull(out);
            new StreamCopier(status, status).transfer(new ByteArrayInputStream(content), out);
            assertEquals(content.length, out.getStatus().getSize(), 0L);
        }
        assertNotNull(test.attributes().getVersionId());
        assertTrue(new DefaultFindFeature(session).find(test));
        assertTrue(new SDSFindFeature(session, nodeid).find(test));
        final PathAttributes attr = new SDSAttributesFinderFeature(session, nodeid).find(test);
        assertEquals(test.attributes().getVersionId(), attr.getVersionId());
        assertEquals(1632127025217L, attr.getModificationDate());
        assertEquals(1632127025217L, new DefaultAttributesFinderFeature(session).find(test).getModificationDate());
        final byte[] compare = new byte[content.length];
        final InputStream stream = new TripleCryptReadFeature(session, nodeid, new SDSReadFeature(session, nodeid)).read(test, new TransferStatus().withLength(content.length), new DisabledConnectionCallback() {
            @Override
            public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
                return new VaultCredentials("eth[oh8uv4Eesij");
            }
        });
        IOUtils.readFully(stream, compare);
        stream.close();
        assertArrayEquals(content, compare);
        String previousVersion = attr.getVersionId();
        // Overwrite
        {
            final byte[] change = RandomUtils.nextBytes(256);
            final TransferStatus status = new TransferStatus();
            status.setLength(change.length);
            final TripleCryptWriteFeature writer = new TripleCryptWriteFeature(session, nodeid, new SDSDirectS3MultipartWriteFeature(session, nodeid));
            final StatusOutputStream<Node> out = writer.write(test, status.exists(true), new DisabledConnectionCallback());
            assertNotNull(out);
            new StreamCopier(status, status).transfer(new ByteArrayInputStream(change), out);
            assertNotEquals(previousVersion, new SDSAttributesAdapter(session).toAttributes(out.getStatus()).getVersionId());
        }
        // Read with previous version must fail
        try {
            test.attributes().withVersionId(previousVersion);
            new TripleCryptReadFeature(session, nodeid, new SDSReadFeature(session, nodeid)).read(test, new TransferStatus(), new DisabledConnectionCallback() {
                @Override
                public Credentials prompt(final Host bookmark, final String title, final String reason, final LoginOptions options) {
                    return new VaultCredentials("eth[oh8uv4Eesij");
                }
            });
            fail();
        }
        catch(NotfoundException e) {
            // Expected
        }
        new SDSDeleteFeature(session, nodeid).delete(Collections.singletonList(room), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }