@Override
    public HttpResponseOutputStream<StorageObject> write(final Path file, final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
        final DelayedHttpEntityCallable<StorageObject> command = new DelayedHttpEntityCallable<StorageObject>(file) {
            @Override
            public StorageObject call(final HttpEntity entity) throws BackgroundException {
                try {
                    // POST /upload/storage/v1/b/myBucket/o
                    final StringBuilder uri = new StringBuilder(String.format("%supload/storage/v1/b/%s/o?uploadType=resumable",
                            session.getClient().getRootUrl(), containerService.getContainer(file).getName()));
                    if(containerService.getContainer(file).attributes().getCustom().containsKey(GoogleStorageAttributesFinderFeature.KEY_REQUESTER_PAYS)) {
                        uri.append(String.format("&userProject=%s", session.getHost().getCredentials().getUsername()));
                    }
                    if(!Acl.EMPTY.equals(status.getAcl())) {
                        if(status.getAcl().isCanned()) {
                            uri.append("&predefinedAcl=");
                            if(Acl.CANNED_PRIVATE.equals(status.getAcl())) {
                                uri.append("private");
                            }
                            else if(Acl.CANNED_PUBLIC_READ.equals(status.getAcl())) {
                                uri.append("publicRead");
                            }
                            else if(Acl.CANNED_PUBLIC_READ_WRITE.equals(status.getAcl())) {
                                uri.append("publicReadWrite");
                            }
                            else if(Acl.CANNED_AUTHENTICATED_READ.equals(status.getAcl())) {
                                uri.append("authenticatedRead");
                            }
                            else if(Acl.CANNED_BUCKET_OWNER_FULLCONTROL.equals(status.getAcl())) {
                                uri.append("bucketOwnerFullControl");
                            }
                            else if(Acl.CANNED_BUCKET_OWNER_READ.equals(status.getAcl())) {
                                uri.append("bucketOwnerRead");
                            }
                            // Reset in status to skip setting ACL in upload filter already applied as canned ACL
                            status.setAcl(Acl.EMPTY);
                        }
                    }
                    final HttpEntityEnclosingRequestBase request = new HttpPost(uri.toString());
                    final StringBuilder metadata = new StringBuilder();
                    metadata.append(String.format("{\"name\": \"%s\"", containerService.getKey(file)));
                    metadata.append(",\"metadata\": {");
                    for(Iterator<Map.Entry<String, String>> iter = status.getMetadata().entrySet().iterator(); iter.hasNext(); ) {
                        final Map.Entry<String, String> item = iter.next();
                        metadata.append(String.format("\"%s\": \"%s\"", item.getKey(), item.getValue()));
                        if(iter.hasNext()) {
                            metadata.append(",");
                        }
                    }
                    metadata.append("}");
                    if(StringUtils.isNotBlank(status.getMime())) {
                        metadata.append(String.format(", \"contentType\": \"%s\"", status.getMime()));
                    }
                    if(StringUtils.isNotBlank(status.getStorageClass())) {
                        metadata.append(String.format(", \"storageClass\": \"%s\"", status.getStorageClass()));
                    }
                    if(null != status.getModified()) {
                        metadata.append(String.format(", \"customTime\": \"%s\"",
                                new ISO8601DateFormatter().format(status.getModified(), TimeZone.getTimeZone("UTC"))));
                    }
                    metadata.append("}");
                    request.setEntity(new StringEntity(metadata.toString(),
                            ContentType.create("application/json", StandardCharsets.UTF_8.name())));
                    if(StringUtils.isNotBlank(status.getMime())) {
                        // Set to the media MIME type of the upload data to be transferred in subsequent requests.
                        request.addHeader("X-Upload-Content-Type", status.getMime());
                    }
                    request.addHeader(HTTP.CONTENT_TYPE, MEDIA_TYPE);
                    final HttpClient client = session.getHttpClient();
                    final HttpResponse response = client.execute(request);
                    try {
                        switch(response.getStatusLine().getStatusCode()) {
                            case HttpStatus.SC_OK:
                                break;
                            default:
                                throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed",
                                        new HttpResponseException(response.getStatusLine().getStatusCode(),
                                                GoogleStorageExceptionMappingService.parse(response)), file);
                        }
                    }
                    finally {
                        EntityUtils.consume(response.getEntity());
                    }
                    if(response.containsHeader(HttpHeaders.LOCATION)) {
                        final String putTarget = response.getFirstHeader(HttpHeaders.LOCATION).getValue();
                        // Upload the file
                        final HttpPut put = new HttpPut(putTarget);
                        put.setEntity(entity);
                        final HttpResponse putResponse = client.execute(put);
                        try {
                            switch(putResponse.getStatusLine().getStatusCode()) {
                                case HttpStatus.SC_OK:
                                case HttpStatus.SC_CREATED:
                                    return session.getClient().getObjectParser().parseAndClose(new InputStreamReader(
                                            putResponse.getEntity().getContent(), StandardCharsets.UTF_8), StorageObject.class);
                                default:
                                    throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed",
                                            new HttpResponseException(putResponse.getStatusLine().getStatusCode(),
                                                    GoogleStorageExceptionMappingService.parse(putResponse)), file);
                            }
                        }
                        finally {
                            EntityUtils.consume(putResponse.getEntity());
                        }
                    }
                    else {
                        throw new DefaultHttpResponseExceptionMappingService().map("Upload {0} failed",
                                new HttpResponseException(response.getStatusLine().getStatusCode(),
                                        GoogleStorageExceptionMappingService.parse(response)), file);
                    }
                }
                catch(IOException e) {
                    throw new GoogleStorageExceptionMappingService().map("Upload {0} failed", e, file);
                }
            }

            @Override
            public long getContentLength() {
                return status.getLength();
            }
        };
        return this.write(file, status, command);
    }
@Test
    public void testWrite() throws Exception {
        final Path container = new Path("cyberduck-test-eu", EnumSet.of(Path.Type.directory, Path.Type.volume));
        final Path test = new Path(container, String.format("%s %s", new AlphanumericRandomStringService().random(), new AlphanumericRandomStringService().random()), EnumSet.of(Path.Type.file));
        {
            final TransferStatus status = new TransferStatus();
            final byte[] content = RandomUtils.nextBytes(2048);
            status.setModified(System.currentTimeMillis());
            status.setLength(content.length);
            status.setMime("application/octet-stream");
            status.setStorageClass("multi_regional");
            status.setMetadata(Collections.singletonMap("c", "d"));
            final HttpResponseOutputStream<StorageObject> out = new GoogleStorageWriteFeature(session).write(test, status, new DisabledConnectionCallback());
            new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out);
            out.close();
            assertNotNull(out.getStatus().getGeneration());
            assertTrue(new GoogleStorageFindFeature(session).find(test));
            final byte[] buffer = new byte[content.length];
            final InputStream in = new GoogleStorageReadFeature(session).read(test, new TransferStatus().withLength(content.length), new DisabledConnectionCallback());
            IOUtils.readFully(in, buffer);
            in.close();
            assertArrayEquals(content, buffer);
        }
        // Overwrite
        {
            final TransferStatus status = new TransferStatus();
            status.setExists(true);
            status.setModified(1530305150672L);
            final byte[] content = RandomUtils.nextBytes(1024);
            status.setLength(content.length);
            final HttpResponseOutputStream<StorageObject> out = new GoogleStorageWriteFeature(session).write(test, status, new DisabledConnectionCallback());
            new StreamCopier(new TransferStatus(), new TransferStatus()).transfer(new ByteArrayInputStream(content), out);
            out.close();
            assertNotNull(out.getStatus().getGeneration());
            final PathAttributes attributes = new GoogleStorageAttributesFinderFeature(session).find(test);
            assertEquals(content.length, attributes.getSize());
        }
        new GoogleStorageDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
    }