/**
  * Returns a {@link File} object that links to the blob
  *
  * @param container
  * @param blobKey
  * @return
  */
 public File getFileForBlobKey(String container, String blobKey) {
   filesystemContainerNameValidator.validate(container);
   filesystemBlobKeyValidator.validate(blobKey);
   String fileName = buildPathStartingFromBaseDir(container, blobKey);
   File blobFile = new File(fileName);
   return blobFile;
 }
  @Override
  public void removeBlob(final String container, final String blobKey) {
    filesystemContainerNameValidator.validate(container);
    filesystemBlobKeyValidator.validate(blobKey);
    String fileName = buildPathStartingFromBaseDir(container, blobKey);
    logger.debug("Deleting blob %s", fileName);
    File fileToBeDeleted = new File(fileName);

    if (fileToBeDeleted.isDirectory()) {
      try {
        UserDefinedFileAttributeView view =
            getUserDefinedFileAttributeView(fileToBeDeleted.toPath());
        if (view != null) {
          for (String s : view.list()) {
            view.delete(s);
          }
        }
      } catch (IOException e) {
        logger.debug("Could not delete attributes from %s: %s", fileToBeDeleted, e);
      }
    }

    try {
      delete(fileToBeDeleted);
    } catch (IOException e) {
      logger.debug("Could not delete %s: %s", fileToBeDeleted, e);
    }

    // now examine if the key of the blob is a complex key (with a directory structure)
    // and eventually remove empty directory
    removeDirectoriesTreeOfBlobKey(container, blobKey);
  }
 @Override
 public boolean blobExists(String container, String key) {
   filesystemContainerNameValidator.validate(container);
   filesystemBlobKeyValidator.validate(key);
   try {
     return buildPathAndChecksIfBlobExists(container, key);
   } catch (IOException e) {
     logger.error(e, "An error occurred while checking key %s in container %s", container, key);
     throw Throwables.propagate(e);
   }
 }
 public Blob newBlob(@ParamValidators({FilesystemBlobKeyValidator.class}) String name) {
   filesystemBlobKeyValidator.validate(name);
   return blobBuilders.get().name(name).build();
 }
  @Override
  public String putBlob(final String containerName, final Blob blob) throws IOException {
    String blobKey = blob.getMetadata().getName();
    Payload payload = blob.getPayload();
    filesystemContainerNameValidator.validate(containerName);
    filesystemBlobKeyValidator.validate(blobKey);
    if (getDirectoryBlobSuffix(blobKey) != null) {
      return putDirectoryBlob(containerName, blob);
    }
    File outputFile = getFileForBlobKey(containerName, blobKey);
    // TODO: should we use a known suffix to filter these out during list?
    String tmpBlobName = blobKey + "-" + UUID.randomUUID();
    File tmpFile = getFileForBlobKey(containerName, tmpBlobName);
    Path tmpPath = tmpFile.toPath();
    HashingInputStream his = null;
    try {
      Files.createParentDirs(tmpFile);
      his = new HashingInputStream(Hashing.md5(), payload.openStream());
      long actualSize = Files.asByteSink(tmpFile).writeFrom(his);
      Long expectedSize = blob.getMetadata().getContentMetadata().getContentLength();
      if (expectedSize != null && actualSize != expectedSize) {
        throw new IOException(
            "Content-Length mismatch, actual: " + actualSize + " expected: " + expectedSize);
      }
      HashCode actualHashCode = his.hash();
      HashCode expectedHashCode = payload.getContentMetadata().getContentMD5AsHashCode();
      if (expectedHashCode != null && !actualHashCode.equals(expectedHashCode)) {
        throw new IOException(
            "MD5 hash code mismatch, actual: " + actualHashCode + " expected: " + expectedHashCode);
      }
      payload.getContentMetadata().setContentMD5(actualHashCode);

      if (outputFile.exists()) {
        delete(outputFile);
      }

      UserDefinedFileAttributeView view = getUserDefinedFileAttributeView(tmpPath);
      if (view != null) {
        try {
          view.write(XATTR_CONTENT_MD5, ByteBuffer.wrap(actualHashCode.asBytes()));
          writeCommonMetadataAttr(view, blob);
        } catch (IOException e) {
          logger.debug("xattrs not supported on %s", tmpPath);
        }
      }

      setBlobAccess(containerName, tmpBlobName, BlobAccess.PRIVATE);

      if (!tmpFile.renameTo(outputFile)) {
        throw new RuntimeException("Could not rename file " + tmpFile + " to " + outputFile);
      }

      return base16().lowerCase().encode(actualHashCode.asBytes());
    } catch (IOException ex) {
      if (tmpFile != null) {
        try {
          delete(tmpFile);
        } catch (IOException e) {
          logger.debug("Could not delete %s: %s", tmpFile, e);
        }
      }
      throw ex;
    } finally {
      closeQuietly(his);
      if (payload != null) {
        payload.release();
      }
    }
  }