@Override
    public FileEntity upload(final Path file, final Local local, final BandwidthThrottle throttle, final StreamListener listener,
                             final TransferStatus status, final ConnectionCallback callback) throws BackgroundException {
        final ThreadPool pool = ThreadPoolFactory.get("multipart", concurrency);
        try {
            // Full size of file
            final long size = status.getLength() + status.getOffset();
            final List<Future<TransferStatus>> parts = new ArrayList<>();
            long offset = 0;
            long remaining = status.getLength();
            String ref = null;
            for(int partNumber = 1; remaining > 0; partNumber++) {
                final FileUploadPartEntity uploadPartEntity = this.continueUpload(file, ref, partNumber);
                final long length;
                if(uploadPartEntity.isParallelParts()) {
                    length = Math.min(Math.max(size / (MAXIMUM_UPLOAD_PARTS - 1), partsize), remaining);
                }
                else {
                    length = remaining;
                }
                parts.add(this.submit(pool, file, local, throttle, listener, status,
                        uploadPartEntity.getUploadUri(), partNumber, offset, length, callback));
                remaining -= length;
                offset += length;
                ref = uploadPartEntity.getRef();
            }
            final List<TransferStatus> checksums = Interruptibles.awaitAll(parts);
            final FileEntity entity = this.completeUpload(file, ref, status, checksums);
            // Mark parent status as complete
            status.withResponse(new BrickAttributesFinderFeature(session).toAttributes(entity)).setComplete();
            return entity;
        }
        finally {
            // Cancel future tasks
            pool.shutdown(false);
        }
    }
@Test
    public void testUploadSmallPart() throws Exception {
        final BrickUploadFeature feature = new BrickUploadFeature(session, new BrickWriteFeature(session), 5 * 1024L * 1024L, 2);
        final Path root = new Path("/", EnumSet.of(Path.Type.directory, Path.Type.volume));
        final String name = new AlphanumericRandomStringService().random();
        final Path test = new Path(root, name, EnumSet.of(Path.Type.file));
        final Local local = new Local(System.getProperty("java.io.tmpdir"), name);
        final int length = 56;
        final byte[] content = RandomUtils.nextBytes(length);
        IOUtils.write(content, local.getOutputStream(false));
        final TransferStatus status = new TransferStatus();
        status.setLength(content.length);
        status.setMime("text/plain");
        final BytecountStreamListener count = new BytecountStreamListener();
        feature.upload(test, local, new BandwidthThrottle(BandwidthThrottle.UNLIMITED),
            count, status, new DisabledLoginCallback());
        assertEquals(content.length, count.getSent());
        assertTrue(status.isComplete());
        assertNotSame(PathAttributes.EMPTY, status.getResponse());
        assertTrue(new BrickFindFeature(session).find(test));
        final PathAttributes attributes = new BrickAttributesFinderFeature(session).find(test);
        assertEquals(content.length, attributes.getSize());
        final byte[] compare = new byte[length];
        IOUtils.readFully(new BrickReadFeature(session).read(test, new TransferStatus().withLength(length), new DisabledConnectionCallback()), compare);
        assertArrayEquals(content, compare);
        new BrickDeleteFeature(session).delete(Collections.singletonList(test), new DisabledLoginCallback(), new Delete.DisabledCallback());
        local.delete();
    }