com.microsoft.azure.storage.file.CloudFile Maven / Gradle / Ivy
Show all versions of azure-storage Show documentation
/**
* Copyright Microsoft Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.microsoft.azure.storage.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import com.microsoft.azure.storage.AccessCondition;
import com.microsoft.azure.storage.Constants;
import com.microsoft.azure.storage.DoesServiceRequest;
import com.microsoft.azure.storage.IPRange;
import com.microsoft.azure.storage.OperationContext;
import com.microsoft.azure.storage.SharedAccessProtocols;
import com.microsoft.azure.storage.StorageCredentials;
import com.microsoft.azure.storage.StorageCredentialsSharedAccessSignature;
import com.microsoft.azure.storage.StorageErrorCode;
import com.microsoft.azure.storage.StorageErrorCodeStrings;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.StorageLocation;
import com.microsoft.azure.storage.StorageUri;
import com.microsoft.azure.storage.blob.BlobRequestOptions;
import com.microsoft.azure.storage.blob.CloudBlob;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.core.Base64;
import com.microsoft.azure.storage.core.BaseResponse;
import com.microsoft.azure.storage.core.ExecutionEngine;
import com.microsoft.azure.storage.core.Logger;
import com.microsoft.azure.storage.core.NetworkInputStream;
import com.microsoft.azure.storage.core.PathUtility;
import com.microsoft.azure.storage.core.RequestLocationMode;
import com.microsoft.azure.storage.core.SR;
import com.microsoft.azure.storage.core.SharedAccessSignatureHelper;
import com.microsoft.azure.storage.core.StorageCredentialsHelper;
import com.microsoft.azure.storage.core.StorageRequest;
import com.microsoft.azure.storage.core.StreamMd5AndLength;
import com.microsoft.azure.storage.core.UriQueryBuilder;
import com.microsoft.azure.storage.core.Utility;
import com.microsoft.azure.storage.core.WrappedByteArrayOutputStream;
/**
* Represents a Microsoft Azure File.
*/
public final class CloudFile implements ListFileItem {
/**
* Holds the number of bytes to buffer when writing to a {@link FileOutputStream}.
*/
protected int streamWriteSizeInBytes = Constants.DEFAULT_STREAM_WRITE_IN_BYTES;
/**
* Holds the minimum read size when using a {@link FileInputStream}.
*/
protected int streamMinimumReadSizeInBytes = Constants.DEFAULT_MINIMUM_READ_SIZE_IN_BYTES;
/**
* Holds the file's share reference.
*/
private CloudFileShare share;
/**
* Represents the file's directory reference.
*/
protected CloudFileDirectory parent;
/**
* Holds the file's name.
*/
private String name;
/**
* Represents the file service client.
*/
protected CloudFileClient fileServiceClient;
/**
* Holds the metadata for the file.
*/
private HashMap metadata = new HashMap();
/**
* Holds the properties of the file.
*/
private FileProperties properties = new FileProperties();
/**
* Stores the absolute URI to the file.
*/
private StorageUri storageUri;
/**
* Creates an instance of the CloudFile
class using the specified absolute URI.
*
* @param fileAbsoluteUri
* A java.net.URI
object that represents the absolute URI to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
public CloudFile(final URI fileAbsoluteUri) throws StorageException, URISyntaxException {
this(new StorageUri(fileAbsoluteUri));
}
/**
* Creates an instance of the CloudFile
class using the specified absolute StorageUri.
*
* @param fileAbsoluteUri
* A {@link StorageUri} object that represents the absolute URI to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
public CloudFile(final StorageUri fileAbsoluteUri) throws StorageException, URISyntaxException {
this(fileAbsoluteUri, null);
}
/**
* Creates an instance of the CloudFile
class using the specified absolute URI
* and credentials.
*
* @param fileAbsoluteUri
* A java.net.URI
object that represents the absolute URI to the file.
* @param credentials
* A {@link StorageCredentials} object used to authenticate access.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
public CloudFile(final URI fileAbsoluteUri, final StorageCredentials credentials) throws StorageException, URISyntaxException {
this(new StorageUri(fileAbsoluteUri), credentials);
}
/**
* Creates an instance of the CloudFile
class using the specified absolute StorageUri
* and credentials.
*
* @param fileAbsoluteUri
* A {@link StorageUri} object that represents the absolute URI to the file.
* @param credentials
* A {@link StorageCredentials} object used to authenticate access.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
public CloudFile(final StorageUri fileAbsoluteUri, final StorageCredentials credentials) throws StorageException, URISyntaxException {
this.parseQueryAndVerify(fileAbsoluteUri, credentials);
}
/**
* Creates an instance of the CloudFile
class by copying values from another cloud file.
*
* @param otherFile
* A CloudFile
object that represents the file to copy.
*/
public CloudFile(final CloudFile otherFile) {
this.metadata = new HashMap();
this.properties = new FileProperties(otherFile.properties);
if (otherFile.metadata != null) {
for (final String key : otherFile.metadata.keySet()) {
this.metadata.put(key, otherFile.metadata.get(key));
}
}
this.storageUri = otherFile.storageUri;
this.share = otherFile.share;
this.parent = otherFile.parent;
this.fileServiceClient = otherFile.fileServiceClient;
this.name = otherFile.name;
this.setStreamMinimumReadSizeInBytes(otherFile.getStreamMinimumReadSizeInBytes());
this.setStreamWriteSizeInBytes(otherFile.getStreamWriteSizeInBytes());
}
/**
* Creates an instance of the CloudFile
class using the specified address, share,
* and client.
*
* @param uri
* A {@link StorageUri} that represents the file directory's address.
* @param fileName
* A String
that represents the name of the file.
* @param share
* A {@link CloudFileShare} object that represents the associated file share.
*/
protected CloudFile(final StorageUri uri, final String fileName, final CloudFileShare share) {
Utility.assertNotNull("uri", uri);
Utility.assertNotNull("fileName", fileName);
Utility.assertNotNull("share", share);
this.name = fileName;
this.fileServiceClient = share.getServiceClient();
this.share = share;
this.storageUri = uri;
}
/**
* Aborts an ongoing Azure File copy operation.
*
* @param copyId
* A String
object that identifies the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void abortCopy(final String copyId) throws StorageException, URISyntaxException {
this.abortCopy(copyId, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Aborts an ongoing Azure File copy operation.
*
* @param copyId
* A String
object that identifies the copy operation.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the Azure File.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void abortCopy(final String copyId, final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.abortCopyImpl(copyId, accessCondition, options), options.getRetryPolicyFactory(), opContext);
}
private StorageRequest abortCopyImpl(final String copyId,
final AccessCondition accessCondition, final FileRequestOptions options) {
Utility.assertNotNull("copyId", copyId);
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.abortCopy(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, copyId);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0, context);
}
@Override
public Void preProcessResponse(CloudFile parentObject, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
return null;
}
};
return putRequest;
}
/**
* Requests the service to start copying a blob's contents, properties, and metadata to a new file.
*
* @param sourceBlob
* A CloudBlob
object that represents the source blob to copy.
*
* @return A String
which represents the copy ID associated with the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final String startCopy(final CloudBlob sourceBlob) throws StorageException, URISyntaxException {
return this.startCopy(sourceBlob, null /* sourceAccessCondition */,
null /* destinationAccessCondition */, null /* options */, null /* opContext */);
}
/**
* Requests the service to start copying a file's contents, properties, and metadata to a new file,
* using the specified access conditions, lease ID, request options, and operation context.
*
* @param sourceBlob
* A CloudBlob
object that represents the source blob to copy.
* @param sourceAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the source blob.
* @param destinationAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the destination file.
* @param options
* A {@link BlobRequestOptions} object that specifies any additional options for the request.
* Specifying null
will use the default request options from the associated
* service client ({@link CloudBlobClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation.
* This object is used to track requests to the storage service, and to provide additional
* runtime information about the operation.
*
* @return A String
which represents the copy ID associated with the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*
*/
@DoesServiceRequest
public final String startCopy(final CloudBlob sourceBlob, final AccessCondition sourceAccessCondition,
final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException, URISyntaxException {
Utility.assertNotNull("sourceBlob", sourceBlob);
URI source = sourceBlob.getSnapshotQualifiedUri();
if (sourceBlob.getServiceClient() != null && sourceBlob.getServiceClient().getCredentials() != null)
{
source = sourceBlob.getServiceClient().getCredentials().transformUri(sourceBlob.getSnapshotQualifiedUri());
}
return this.startCopy(source, sourceAccessCondition, destinationAccessCondition, options, opContext);
}
/**
* Requests the service to start copying an Azure File's contents, properties, and metadata to a new Azure File.
*
* @param sourceFile
* A CloudFile
object that represents the source Azure File to copy.
*
* @return A String
which represents the copy ID associated with the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final String startCopy(final CloudFile sourceFile) throws StorageException, URISyntaxException {
return this.startCopy(sourceFile, null /* sourceAccessCondition */,
null /* destinationAccessCondition */, null /* options */, null /* opContext */);
}
/**
* Requests the service to start copying an Azure File's contents, properties, and metadata to a new Azure File,
* using the specified access conditions, lease ID, request options, and operation context.
*
* @param sourceFile
* A CloudFile
object that represents the source file to copy.
* @param sourceAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the source.
* @param destinationAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the destination.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request.
* Specifying null
will use the default request options from the associated
* service client ({@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation.
* This object is used to track requests to the storage service, and to provide additional
* runtime information about the operation.
*
* @return A String
which represents the copy ID associated with the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*
*/
@DoesServiceRequest
public final String startCopy(final CloudFile sourceFile, final AccessCondition sourceAccessCondition,
final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException, URISyntaxException {
Utility.assertNotNull("sourceFile", sourceFile);
return this.startCopy(sourceFile.getTransformedAddress(opContext).getPrimaryUri(),
sourceAccessCondition, destinationAccessCondition, options, opContext);
}
/**
* Requests the service to start copying a URI's contents, properties, and metadata to a new Azure File.
*
* @param source
* The source's java.net.URI
.
*
* @return A String
which represents the copy ID associated with the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final String startCopy(final URI source) throws StorageException, URISyntaxException {
return this.startCopy(source, null /* sourceAccessCondition */,
null /* destinationAccessCondition */, null /* options */, null /* opContext */);
}
/**
* Requests the service to start copying a URI's contents, properties, and metadata to a new Azure File,
* using the specified access conditions, lease ID, request options, and operation context.
*
* @param source
* The source's java.net.URI
.
* @param sourceAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the source.
* @param destinationAccessCondition
* An {@link AccessCondition} object that represents the access conditions for the destination.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request.
* Specifying null
will use the default request options from the associated
* service client ({@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation.
* This object is used to track requests to the storage service, and to provide additional
* runtime information about the operation.
*
* @return A String
which represents the copy ID associated with the copy operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*
*/
@DoesServiceRequest
public final String startCopy(final URI source, final AccessCondition sourceAccessCondition,
final AccessCondition destinationAccessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.startCopyImpl(source, sourceAccessCondition, destinationAccessCondition, options),
options.getRetryPolicyFactory(), opContext);
}
private StorageRequest startCopyImpl(final URI source,
final AccessCondition sourceAccessCondition, final AccessCondition destinationAccessCondition,
final FileRequestOptions options) {
if (sourceAccessCondition != null && !Utility.isNullOrEmpty(sourceAccessCondition.getLeaseID())) {
throw new IllegalArgumentException(SR.LEASE_CONDITION_ON_SOURCE);
}
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.copyFrom(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, sourceAccessCondition, destinationAccessCondition, source.toASCIIString());
}
@Override
public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
FileRequest.addMetadata(connection, file.metadata, context);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0, context);
}
@Override
public String preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
file.properties.setCopyState(FileResponse.getCopyState(this.getConnection()));
return file.properties.getCopyState().getCopyId();
}
};
return putRequest;
}
/**
* Clears a range from a file.
*
* Calling clearRange
releases the storage space used by the specified range. Ranges that have been
* cleared are no longer tracked as part of the file.
*
* @param offset
* The offset, in bytes, at which to begin clearing.
* @param length
* The length, in bytes, of the data range to be cleared.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void clearRange(final long offset, final long length) throws StorageException, URISyntaxException {
this.clearRange(offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Clears a range from a file using the specified lease ID, request options, and operation context.
*
* Calling clearRange
releases the storage space used by the specified range. Ranges that have been
* cleared are no longer tracked as part of the file.
*
* @param offset
* A long
which represents the offset, in bytes, at which to begin clearing.
* @param length
* A long
which represents the length, in bytes, of the data range to be cleared.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void clearRange(final long offset, final long length, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
final FileRange range = new FileRange(offset, offset + length - 1);
this.putRangeInternal(range, FileRangeOperationType.CLEAR, null, length, null, accessCondition, options,
opContext);
}
/**
* Creates a file. If the file already exists, this will replace it.
*
* @param size
* A long
which represents the size, in bytes, of the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void create(final long size) throws StorageException, URISyntaxException {
this.create(size, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Creates a file using the specified access condition, request options and operation context. If the file already
* exists, this will replace it.
*
* @param size
* A long
which represents the size, in bytes, of the file.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void create(final long size, final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.createImpl(size, accessCondition, options),
options.getRetryPolicyFactory(), opContext);
}
private StorageRequest createImpl(final long size,
final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.putFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, file.properties, size);
}
@Override
public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
FileRequest.addMetadata(connection, file.metadata, context);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
return null;
}
};
return putRequest;
}
/**
* Deletes the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void delete() throws StorageException, URISyntaxException {
this.delete(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Deletes the file using the specified access condition, request options, and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void delete(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.deleteImpl(accessCondition, options),
options.getRetryPolicyFactory(), opContext);
}
/**
* Deletes the file if it exists.
*
* @return true
if the file was deleted; otherwise, false
.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*
*/
@DoesServiceRequest
public final boolean deleteIfExists() throws StorageException, URISyntaxException {
return this.deleteIfExists(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Deletes the file if it exists, using the specified access condition, request options, and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return true
if the file existed and was deleted; otherwise, false
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final boolean deleteIfExists(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
this.getShare().assertNoSnapshot();
boolean exists = this.exists(true, accessCondition, options, opContext);
if (exists) {
try {
this.delete(accessCondition, options, opContext);
return true;
}
catch (StorageException e) {
if (e.getHttpStatusCode() == HttpURLConnection.HTTP_NOT_FOUND
&& StorageErrorCodeStrings.RESOURCE_NOT_FOUND.equals(e.getErrorCode())) {
return false;
}
else {
throw e;
}
}
}
else {
return false;
}
}
private StorageRequest deleteImpl(final AccessCondition accessCondition,
final FileRequestOptions options) {
final StorageRequest deleteRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.deleteFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_ACCEPTED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
return null;
}
};
return deleteRequest;
}
/**
* Downloads the contents of a file to a stream.
*
* @param outStream
* An {@link OutputStream}
object that represents the target stream.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void download(final OutputStream outStream) throws StorageException {
this.download(outStream, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Downloads the contents of a file to a stream using the specified request options and operation context.
*
* @param outStream
* An OutputStream
object that represents the target stream.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void download(final OutputStream outStream, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.downloadToStreamImpl(
null /* fileOffset */, null /* length */, outStream, accessCondition, options, opContext), options
.getRetryPolicyFactory(), opContext);
}
/**
* Downloads the contents of a file to a stream.
*
* @param offset
* A long
which represents the offset to use as the starting point for the source.
* @param length
* A Long
which represents the number of bytes to read or null
.
* @param outStream
* An {@link OutputStream}
object that represents the target stream.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void downloadRange(final long offset, final Long length, final OutputStream outStream)
throws StorageException {
this.downloadRange(offset, length, outStream, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Downloads the contents of a file to a stream using the specified request options and operation context.
*
* @param offset
* A long
which represents the offset to use as the starting point for the source.
* @param length
* A Long
which represents the number of bytes to read or null
.
* @param outStream
* An {@link OutputStream}
object that represents the target stream.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void downloadRange(final long offset, final Long length, final OutputStream outStream,
final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException {
if (offset < 0 || (length != null && length <= 0)) {
throw new IndexOutOfBoundsException();
}
if (opContext == null) {
opContext = new OperationContext();
}
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.downloadToStreamImpl(offset, length, outStream, accessCondition, options, opContext),
options.getRetryPolicyFactory(), opContext);
}
/**
* Downloads a range of bytes from the file to the given byte buffer.
*
* @param fileOffset
* A long
which represents the offset within the file to begin downloading.
* @param length
* A Long
which represents the number of bytes to read.
* @param buffer
* A byte
array which represents the buffer to write to.
* @param bufferOffset
* An int
which represents the offset in the byte buffer to begin writing.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request.
* @param opContext
* An {@link OperationContext} object used to track the execution of the operation.
* @throws StorageException
* an exception representing any error which occurred during the operation.
*/
@DoesServiceRequest
protected final int downloadRangeInternal(final long fileOffset, final Long length, final byte[] buffer,
final int bufferOffset, final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
if (bufferOffset < 0 || fileOffset < 0 || (length != null && length <= 0)) {
throw new IndexOutOfBoundsException();
}
if (opContext == null) {
opContext = new OperationContext();
}
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
WrappedByteArrayOutputStream outputStream = new WrappedByteArrayOutputStream(buffer, bufferOffset);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.downloadToStreamImpl(fileOffset, length, outputStream, accessCondition, options, opContext),
options.getRetryPolicyFactory(), opContext);
return outputStream.getPosition();
}
/**
* Downloads a range of bytes from the file to the given byte buffer.
*
* @param offset
* A long
which represents the byte offset to use as the starting point for the source.
* @param length
* A Long
which represents the number of bytes to read or null.
* @param buffer
* A byte
array which represents the buffer to which the file bytes are downloaded.
* @param bufferOffset
* An int
which represents the byte offset to use as the starting point for the target.
*
* @throws StorageException
*/
@DoesServiceRequest
public final int downloadRangeToByteArray(final long offset, final Long length, final byte[] buffer,
final int bufferOffset) throws StorageException {
return this.downloadRangeToByteArray(offset, length, buffer, bufferOffset, null /* accessCondition */,
null /* options */, null /* opContext */);
}
/**
* Downloads a range of bytes from the file to the given byte buffer, using the specified request options and
* operation context.
*
* @param offset
* A long
which represents the byte offset to use as the starting point for the source.
* @param length
* A Long
which represents the number of bytes to read or null
.
* @param buffer
* A byte
array which represents the buffer to which the file bytes are downloaded.
* @param bufferOffset
* An int
which represents the byte offset to use as the starting point for the target.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final int downloadRangeToByteArray(final long offset, final Long length, final byte[] buffer,
final int bufferOffset, final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
Utility.assertNotNull("buffer", buffer);
if (length != null) {
if (length + bufferOffset > buffer.length) {
throw new IndexOutOfBoundsException();
}
}
if (opContext == null) {
opContext = new OperationContext();
}
opContext.initialize();
return this.downloadRangeInternal(offset, length, buffer, bufferOffset, accessCondition, options, opContext);
}
/**
* Downloads a range of bytes from the file to the given byte buffer.
*
* @param buffer
* A byte
array which represents the buffer to which the file bytes are downloaded.
* @param bufferOffset
* An int
which represents the byte offset to use as the starting point for the target.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final int downloadToByteArray(final byte[] buffer, final int bufferOffset) throws StorageException {
return this
.downloadToByteArray(buffer, bufferOffset, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Downloads a range of bytes from the file to the given byte buffer, using the specified request options and
* operation context.
*
* @param buffer
* A byte
array which represents the buffer to which the file bytes are downloaded.
* @param bufferOffset
* A long
which represents the byte offset to use as the starting point for the target.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final int downloadToByteArray(final byte[] buffer, final int bufferOffset,
final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException {
Utility.assertNotNull("buffer", buffer);
if (bufferOffset < 0) {
throw new IndexOutOfBoundsException();
}
if (bufferOffset >= buffer.length) {
throw new IndexOutOfBoundsException();
}
if (opContext == null) {
opContext = new OperationContext();
}
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
WrappedByteArrayOutputStream outputStream = new WrappedByteArrayOutputStream(buffer, bufferOffset);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.downloadToStreamImpl(null, null, outputStream, accessCondition, options, opContext),
options.getRetryPolicyFactory(), opContext);
return outputStream.getPosition();
}
/**
* Downloads a file.
*
* @param path
* A String
which represents the path to the file that will be created.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
*/
public void downloadToFile(final String path) throws StorageException, IOException {
downloadToFile(path, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Downloads a file.
*
* @param path
* A String
which represents the path to the file that will be created.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
*/
public void downloadToFile(final String path, final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, IOException {
OutputStream outputStream = new BufferedOutputStream(new java.io.FileOutputStream(path));
try {
this.download(outputStream, accessCondition, options, opContext);
outputStream.close();
}
catch (StorageException e) {
deleteEmptyFileOnException(outputStream, path);
throw e;
}
catch (IOException e) {
deleteEmptyFileOnException(outputStream, path);
throw e;
}
}
/**
* Helper to delete an empty file in the case of an exception
*
* @param outputStream
* @param path
* @throws IOException
*/
private void deleteEmptyFileOnException(OutputStream outputStream, String path) {
try {
outputStream.close();
File fileToDelete = new File(path);
fileToDelete.delete();
}
catch (Exception e) {
// Best effort delete.
}
}
/**
* Downloads a file to a string using the platform's default encoding.
*
* @return A String
which represents the file's contents.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
*/
public String downloadText() throws StorageException, IOException {
return this
.downloadText(null /* charsetName */, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Downloads a file to a string using the specified encoding.
*
* @param charsetName
* A String
which represents the name of the charset to use to encode the content.
* If null, the platform's default encoding is used.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return A String
which represents the file's contents.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
*/
public String downloadText(final String charsetName, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException, IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
this.download(baos, accessCondition, options, opContext);
return charsetName == null ? baos.toString() : baos.toString(charsetName);
}
/**
* Returns a collection of file ranges and their starting and ending byte offsets.
*
* The start and end byte offsets for each file range are inclusive.
*
* @return An ArrayList
object which represents the set of file ranges and their starting and ending
* byte offsets.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public ArrayList downloadFileRanges() throws StorageException {
return this.downloadFileRanges(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Returns a collection of file ranges and their starting and ending byte offsets using the specified request
* options and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return An ArrayList
object which represents the set of file ranges and their starting
* and ending byte offsets.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public ArrayList downloadFileRanges(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.downloadFileRangesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
}
private StorageRequest> downloadFileRangesImpl(
final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest> getRequest =
new StorageRequest>(options, this.getStorageUri()) {
@Override
public void setRequestLocationMode() {
this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
}
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.getFileRanges(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, file.getShare().snapshotID);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
}
@Override
public ArrayList preProcessResponse(CloudFile parentObject, CloudFileClient client,
OperationContext context) throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
}
return null;
}
@Override
public ArrayList postProcessResponse(HttpURLConnection connection, CloudFile file,
CloudFileClient client, OperationContext context, ArrayList storageObject)
throws Exception {
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
file.updateLengthFromResponse(this.getConnection());
return FileRangeHandler.getFileRanges(this.getConnection().getInputStream());
}
};
return getRequest;
}
@DoesServiceRequest
private final StorageRequest downloadToStreamImpl(final Long fileOffset,
final Long length, final OutputStream outStream, final AccessCondition accessCondition,
final FileRequestOptions options, OperationContext opContext) {
final long startingOffset = fileOffset == null ? 0 : fileOffset;
final StorageRequest getRequest = new StorageRequest(
options, this.getStorageUri()) {
@Override
public void setRequestLocationMode() {
this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
}
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
// The first time this is called, we have to set the length and offset.
// On retries, these will already have values and need not be called.
if (this.getOffset() == null) {
this.setOffset(fileOffset);
}
if (this.getLength() == null) {
this.setLength(length);
}
// Only do this when we have support from the service.
// AccessCondition tempCondition = (this.getETagLockCondition() != null) ? this.getETagLockCondition()
// : accessCondition;
return FileRequest.getFile(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, file.getShare().snapshotID, this.getOffset(), this.getLength(),
(options.getUseTransactionalContentMD5() && !this.getArePropertiesPopulated()));
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
}
@Override
public Integer preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_PARTIAL
&& this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
if (!this.getArePropertiesPopulated()) {
final FileAttributes retrievedAttributes = FileResponse.getFileAttributes(this.getConnection(),
file.getStorageUri());
file.properties = retrievedAttributes.getProperties();
file.metadata = retrievedAttributes.getMetadata();
// Need to store the Content MD5 in case we fail part way through.
// We would still need to verify the entire range.
this.setContentMD5(this.getConnection().getHeaderField(Constants.HeaderConstants.CONTENT_MD5));
if (!options.getDisableContentMD5Validation() && options.getUseTransactionalContentMD5()
&& Utility.isNullOrEmpty(this.getContentMD5())) {
throw new StorageException(StorageErrorCodeStrings.MISSING_MD5_HEADER, SR.MISSING_MD5,
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
this.setLockedETag(file.properties.getEtag());
this.setArePropertiesPopulated(true);
}
else {
if (this.getLockedETag() != null) {
if (!this.getLockedETag().equals(file.properties.getEtag())) {
throw new StorageException(StorageErrorCode.CONDITION_FAILED.toString(),
SR.INVALID_CONDITIONAL_HEADERS, HttpURLConnection.HTTP_PRECON_FAILED, null, null);
}
}
}
// If the download fails and we need to resume the download, going to the
// same storage location is important to prevent a possible ETag mismatch.
this.setRequestLocationMode((this.getResult().getTargetLocation() == StorageLocation.PRIMARY) ?
RequestLocationMode.PRIMARY_ONLY : RequestLocationMode.SECONDARY_ONLY);
return null;
}
@Override
public Integer postProcessResponse(HttpURLConnection connection, CloudFile file, CloudFileClient client,
OperationContext context, Integer storageObject) throws Exception {
final Boolean validateMD5 = !options.getDisableContentMD5Validation()
&& !Utility.isNullOrEmpty(this.getContentMD5());
final String contentLength = connection.getHeaderField(Constants.HeaderConstants.CONTENT_LENGTH);
final long expectedLength = Long.parseLong(contentLength);
Logger.info(context, String.format(SR.CREATING_NETWORK_STREAM, expectedLength));
final NetworkInputStream streamRef = new NetworkInputStream(connection.getInputStream(), expectedLength);
try {
// writeToOutputStream will update the currentRequestByteCount on this request in case a retry
// is needed and download should resume from that point
final StreamMd5AndLength descriptor = Utility.writeToOutputStream(streamRef, outStream, -1, false,
validateMD5, context, options, true, this, this.getCurrentDescriptor());
// length was already checked by the NetworkInputStream, now check Md5
if (validateMD5 && !this.getContentMD5().equals(descriptor.getMd5())) {
throw new StorageException(StorageErrorCodeStrings.INVALID_MD5, String.format(
SR.FILE_HASH_MISMATCH, this.getContentMD5(), descriptor.getMd5()),
Constants.HeaderConstants.HTTP_UNUSED_306, null, null);
}
}
finally {
// Close the stream and return. Closing an already closed stream is harmless. So its fine to try
// to drain the response and close the stream again in the executor.
streamRef.close();
}
return null;
}
@Override
public void recoveryAction(OperationContext context) throws IOException {
if (this.getETagLockCondition() == null && (!Utility.isNullOrEmpty(this.getLockedETag()))) {
AccessCondition etagLockCondition = new AccessCondition();
etagLockCondition.setIfMatch(this.getLockedETag());
if (accessCondition != null) {
etagLockCondition.setLeaseID(accessCondition.getLeaseID());
}
this.setETagLockCondition(etagLockCondition);
}
if (this.getCurrentRequestByteCount() > 0) {
this.setOffset(startingOffset + this.getCurrentRequestByteCount());
if (length != null) {
this.setLength(length - this.getCurrentRequestByteCount());
}
}
}
};
return getRequest;
}
/**
* Populates a file's properties and metadata.
*
* This method populates the file's system properties and user-defined metadata. Before reading or modifying
* a file's properties or metadata, call this method or its overload to retrieve the latest values for the
* file's properties and metadata from the Microsoft Azure storage service.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void downloadAttributes() throws StorageException {
this.downloadAttributes(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Populates a file's properties and metadata using the specified request options and operation context.
*
* This method populates the file's system properties and user-defined metadata. Before reading or modifying
* a file's properties or metadata, call this method or its overload to retrieve the latest values for
* the file's properties and metadata from the Microsoft Azure storage service.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final void downloadAttributes(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.downloadAttributesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
}
private StorageRequest downloadAttributesImpl(
final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest getRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public void setRequestLocationMode() {
this.setRequestLocationMode(RequestLocationMode.PRIMARY_OR_SECONDARY);
}
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.getFileProperties(
file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, file.getShare().snapshotID);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
// Set attributes
final FileAttributes retrievedAttributes = FileResponse.getFileAttributes(this.getConnection(),
file.getStorageUri());
file.properties = retrievedAttributes.getProperties();
file.metadata = retrievedAttributes.getMetadata();
return null;
}
};
return getRequest;
}
/**
* Checks to see if the file exists.
*
* @return true
if the file exists, otherwise false
.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final boolean exists() throws StorageException {
return this.exists(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Checks to see if the file exists, using the specified access condition, request options and operation context.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return true
if the file exists, otherwise false
.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final boolean exists(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
return this.exists(false /* primaryOnly */, accessCondition, options, opContext);
}
@DoesServiceRequest
private final boolean exists(final boolean primaryOnly, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
return ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.existsImpl(primaryOnly, accessCondition, options), options.getRetryPolicyFactory(), opContext);
}
private StorageRequest existsImpl(final boolean primaryOnly,
final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest getRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public void setRequestLocationMode() {
this.setRequestLocationMode(primaryOnly ? RequestLocationMode.PRIMARY_ONLY
: RequestLocationMode.PRIMARY_OR_SECONDARY);
}
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.getFileProperties(
file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, file.getShare().snapshotID);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, -1L, context);
}
@Override
public Boolean preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_OK) {
final FileAttributes retrievedAttributes = FileResponse.getFileAttributes(this.getConnection(),
file.getStorageUri());
file.properties = retrievedAttributes.getProperties();
file.metadata = retrievedAttributes.getMetadata();
return Boolean.valueOf(true);
}
else if (this.getResult().getStatusCode() == HttpURLConnection.HTTP_NOT_FOUND) {
return Boolean.valueOf(false);
}
else {
this.setNonExceptionedRetryableFailure(true);
// return false instead of null to avoid SCA issues
return false;
}
}
};
return getRequest;
}
/**
* Returns a shared access signature for the file using the specified group policy identifier and
* shared access file headers. Note this does not contain the leading "?".
*
* @param policy
* A {@link SharedAccessFilePolicy}
object that represents the access policy for the shared
* access signature.
* @param groupPolicyIdentifier
* A String
that represents the share-level access policy.
*
* @return A String
that represents the shared access signature.
*
* @throws InvalidKeyException
* If the credentials are invalid.
* @throws StorageException
* If a storage service error occurred.
*/
public String generateSharedAccessSignature(final SharedAccessFilePolicy policy, final String groupPolicyIdentifier)
throws InvalidKeyException, StorageException {
return generateSharedAccessSignature(policy, null /* headers */, groupPolicyIdentifier);
}
/**
* Returns a shared access signature for the file using the specified group policy identifier and
* shared access file headers. Note this does not contain the leading "?".
*
* @param policy
* A {@link SharedAccessFilePolicy}
object that represents the access policy for the shared
* access signature.
* @param headers
* A {@link SharedAccessFileHeaders}
object that represents the optional header values to
* set for a file accessed with this shared access signature.
* @param groupPolicyIdentifier
* A String
that represents the share-level access policy.
*
* @return A String
that represents the shared access signature.
*
* @throws IllegalArgumentException
* If the credentials are invalid.
* @throws InvalidKeyException
* If the credentials are invalid.
* @throws StorageException
* If a storage service error occurred.
*/
public String generateSharedAccessSignature(
final SharedAccessFilePolicy policy, final SharedAccessFileHeaders headers, final String groupPolicyIdentifier)
throws InvalidKeyException, StorageException {
return this.generateSharedAccessSignature(policy, headers, groupPolicyIdentifier,
null /* IP range */, null /* protocols */);
}
/**
* Returns a shared access signature for the file using the specified group policy identifier and
* shared access file headers. Note this does not contain the leading "?".
*
* @param policy
* A {@link SharedAccessFilePolicy}
object that represents the access policy for the shared
* access signature.
* @param headers
* A {@link SharedAccessFileHeaders}
object that represents the optional header values to
* set for a file accessed with this shared access signature.
* @param groupPolicyIdentifier
* A String
that represents the share-level access policy.
* @param ipRange
* A {@link IPRange} object containing the range of allowed IP addresses.
* @param protocols
* A {@link SharedAccessProtocols} representing the allowed Internet protocols.
*
* @return A String
that represents the shared access signature.
*
* @throws IllegalArgumentException
* If the credentials are invalid.
* @throws InvalidKeyException
* If the credentials are invalid.
* @throws StorageException
* If a storage service error occurred.
*/
public String generateSharedAccessSignature(
final SharedAccessFilePolicy policy, final SharedAccessFileHeaders headers,
final String groupPolicyIdentifier, final IPRange ipRange, final SharedAccessProtocols protocols)
throws InvalidKeyException, StorageException {
if (!StorageCredentialsHelper.canCredentialsSignRequest(this.fileServiceClient.getCredentials())) {
throw new IllegalArgumentException(SR.CANNOT_CREATE_SAS_WITHOUT_ACCOUNT_KEY);
}
final String signature = SharedAccessSignatureHelper.generateSharedAccessSignatureHashForBlobAndFile(
policy, headers, groupPolicyIdentifier, this.getCanonicalName(),
ipRange, protocols, this.fileServiceClient);
final UriQueryBuilder builder = SharedAccessSignatureHelper.generateSharedAccessSignatureForBlobAndFile(
policy, headers, groupPolicyIdentifier, "f", ipRange, protocols, signature);
return builder.toString();
}
/**
* Returns the canonical name of the file in the format of
* /<service-name>/<account-name>/<share-name>/<file-name>.
*
* This format is used for Shared Access operations.
*
* @return The canonical name in the format of /<service-name>/<account-name>
* /<share-name>/<file-name>.
*/
String getCanonicalName() {
StringBuilder canonicalName = new StringBuilder("/");
canonicalName.append(SR.FILE);
String rawPath = this.getUri().getRawPath();
if (this.fileServiceClient.isUsePathStyleUris()) {
canonicalName.append(rawPath);
}
else {
canonicalName.append(PathUtility.getCanonicalPathFromCredentials(
this.getServiceClient().getCredentials(), rawPath));
}
return canonicalName.toString();
}
/**
* Returns the Azure File's copy state.
*
* @return A {@link CopyState} object that represents the copy state of the file.
*/
public CopyState getCopyState() {
return this.properties.getCopyState();
}
/**
* Opens a file input stream to download the file.
*
* Use {@link CloudFile#setStreamMinimumReadSizeInBytes(int)} to configure the read size.
*
* @return An InputStream
object that represents the stream to use for reading from the file.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final FileInputStream openRead() throws StorageException {
return this.openRead(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Opens a file input stream to download the file using the specified request options and
* operation context.
*
* Use {@link #setStreamMinimumReadSizeInBytes(int)} to configure the read size.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return An InputStream
object that represents the stream to use for reading from the file.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
public final FileInputStream openRead(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException {
if (opContext == null) {
opContext = new OperationContext();
}
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient, false /* setStartTime */);
return new FileInputStream(this, accessCondition, options, opContext);
}
/**
* Opens an output stream object to write data to the file. The file must already exist and any existing data may
* be overwritten.
*
* @return A {@link FileOutputStream} object used to write data to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public FileOutputStream openWriteExisting() throws StorageException, URISyntaxException {
return this
.openOutputStreamInternal(null /* length */, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Opens an output stream object to write data to the file, using specified request options and
* operation context. The file must already exist and any existing data may be overwritten.
*
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return A {@link FileOutputStream} object used to write data to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public FileOutputStream openWriteExisting(AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
return this.openOutputStreamInternal(null /* length */, accessCondition, options, opContext);
}
/**
* Opens an output stream object to write data to the file. The file does not yet exist and will
* be created with the length specified. If the file already exists on the service, it will be overwritten.
*
* To avoid overwriting and instead throw an error, please use the
* {@link #openWriteNew(long, AccessCondition, FileRequestOptions, OperationContext)} overload with the appropriate
* {@link AccessCondition}.
*
* @param length
* A long
which represents the length, in bytes, of the stream to create.
*
* @return A {@link FileOutputStream} object used to write data to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public FileOutputStream openWriteNew(final long length) throws StorageException, URISyntaxException {
return this
.openOutputStreamInternal(length, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Opens an output stream object to write data to the file, using the specified lease ID, request options and
* operation context. The file does not need to yet exist and will be created with the length specified. If the file
* already exists on the service, it will be overwritten.
*
* To avoid overwriting and instead throw an error, please pass in an {@link AccessCondition} generated using
* {@link AccessCondition#generateIfNotExistsCondition()}.
*
* @param length
* A long
which represents the length, in bytes, of the stream to create.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return A {@link FileOutputStream} object used to write data to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public FileOutputStream openWriteNew(final long length, AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException {
return openOutputStreamInternal(length, accessCondition, options, opContext);
}
/**
* Opens an output stream object to write data to the file, using the specified lease ID, request options and
* operation context. If the length is specified, a new file will be created with the length specified.
* Otherwise, the file must already exist and a stream of its current length will be opened.
*
* @param length
* A long
which represents the length, in bytes, of the stream to create. This value must be
* null if the file already exists.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return A {@link FileOutputStream} object used to write data to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
private FileOutputStream openOutputStreamInternal(Long length, AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient, false /* setStartTime */);
if (length != null) {
this.create(length, accessCondition, options, opContext);
}
else {
if (options.getStoreFileContentMD5()) {
throw new IllegalArgumentException(SR.FILE_MD5_NOT_POSSIBLE);
}
this.downloadAttributes(accessCondition, options, opContext);
length = this.getProperties().getLength();
}
if (accessCondition != null) {
accessCondition = AccessCondition.generateLeaseCondition(accessCondition.getLeaseID());
}
return new FileOutputStream(this, length, accessCondition, options, opContext);
}
/**
* Uploads a file from data in a byte array. If the file already exists on the service, it will be overwritten.
*
* @param buffer
* A byte
array which represents the data to write to the file.
* @param offset
* A int
which represents the offset of the byte array from which to start the data upload.
* @param length
* An int
which represents the number of bytes to upload from the input buffer.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
* @throws URISyntaxException
*/
public void uploadFromByteArray(final byte[] buffer, final int offset, final int length) throws StorageException,
IOException, URISyntaxException {
uploadFromByteArray(buffer, offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Uploads a file from data in a byte array. If the file already exists on the service, it will be overwritten.
*
* @param buffer
* A byte
array which represents the data to write to the file.
* @param offset
* A int
which represents the offset of the byte array from which to start the data upload.
* @param length
* An int
which represents the number of bytes to upload from the input buffer.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
* @throws URISyntaxException
*/
public void uploadFromByteArray(final byte[] buffer, final int offset, final int length,
final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException, IOException, URISyntaxException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(buffer, offset, length);
this.upload(inputStream, length, accessCondition, options, opContext);
inputStream.close();
}
/**
* Uploads a local file. If the file already exists on the service, it will be overwritten.
*
* @param path
* A String
which represents the path to the file to be uploaded.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
* @throws URISyntaxException
*/
public void uploadFromFile(final String path) throws StorageException, IOException, URISyntaxException {
uploadFromFile(path, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Uploads a file from a local file. If the file already exists on the service, it will be overwritten.
*
* @param path
* A String
which represents the path to the file to be uploaded.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
* @throws URISyntaxException
*/
public void uploadFromFile(final String path, final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, IOException, URISyntaxException {
File file = new File(path);
long fileLength = file.length();
InputStream inputStream = new BufferedInputStream(new java.io.FileInputStream(file));
this.upload(inputStream, fileLength, accessCondition, options, opContext);
inputStream.close();
}
/**
* Uploads a file from a string using the platform's default encoding. If the file already exists on the service, it
* will be overwritten.
*
* @param content
* A String
which represents the content that will be uploaded to the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
* @throws URISyntaxException
*/
public void uploadText(final String content) throws StorageException, IOException, URISyntaxException {
this.uploadText(content, null /* charsetName */, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Uploads a file from a string using the specified encoding. If the file already exists on the service, it will be
* overwritten.
*
* @param content
* A String
which represents the content that will be uploaded to the file.
* @param charsetName
* A String
which represents the name of the charset to use to encode the content.
* If null, the platform's default encoding is used.
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws IOException
* @throws URISyntaxException
*/
public void uploadText(final String content, final String charsetName, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException, IOException, URISyntaxException {
byte[] bytes = (charsetName == null) ? content.getBytes() : content.getBytes(charsetName);
this.uploadFromByteArray(bytes, 0, bytes.length, accessCondition, options, opContext);
}
/**
* Uploads a range to a file.
*
* @param sourceStream
* An {@link InputStream} object which represents the input stream to write to the file.
* @param offset
* A long
which represents the offset, in number of bytes, at which to begin writing the
* data.
* @param length
* A long
which represents the length, in bytes, of the data to write.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void uploadRange(final InputStream sourceStream, final long offset, final long length)
throws StorageException, IOException, URISyntaxException {
this.uploadRange(sourceStream, offset, length, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Uploads a range to a file using the specified lease ID, request options, and operation context.
*
* @param sourceStream
* An {@link InputStream} object which represents the input stream to write to the file.
* @param offset
* A long
which represents the offset, in number of bytes, at which to begin writing the
* data.
* @param length
* A long
which represents the length, in bytes, of the data to write.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void uploadRange(final InputStream sourceStream, final long offset, final long length,
final AccessCondition accessCondition, FileRequestOptions options, OperationContext opContext)
throws StorageException, IOException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
final FileRange range = new FileRange(offset, offset + length - 1);
final byte[] data = new byte[(int) length];
String md5 = null;
int count = 0;
int total = 0;
while (total < length) {
count = sourceStream.read(data, total, (int) Math.min(length - total, Integer.MAX_VALUE));
total += count;
}
if (options.getUseTransactionalContentMD5()) {
try {
final MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(data, 0, data.length);
md5 = Base64.encode(digest.digest());
}
catch (final NoSuchAlgorithmException e) {
// This wont happen, throw fatal.
throw Utility.generateNewUnexpectedStorageException(e);
}
}
this.putRangeInternal(range, FileRangeOperationType.UPDATE, data, length, md5, accessCondition, options,
opContext);
}
/**
* Used for both uploadRange and clearRange.
*
* @param range
* A {@link FileRange} object that specifies the file range.
* @param operationType
* A {@link FileRangeOperationType} enumeration value that specifies the operation type.
* @param data
* A byte
array which represents the data to write.
* @param length
* A long
which represents the number of bytes to write.
* @param md5
* A String
which represents the MD5 hash for the data.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
*/
@DoesServiceRequest
private void putRangeInternal(final FileRange range, final FileRangeOperationType operationType, final byte[] data,
final long length, final String md5, final AccessCondition accessCondition,
final FileRequestOptions options, final OperationContext opContext) throws StorageException {
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
putRangeImpl(range, operationType, data, length, md5, accessCondition, options, opContext),
options.getRetryPolicyFactory(), opContext);
}
private StorageRequest putRangeImpl(final FileRange range,
final FileRangeOperationType operationType, final byte[] data, final long length, final String md5,
final AccessCondition accessCondition, final FileRequestOptions options, final OperationContext opContext) {
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
if (operationType == FileRangeOperationType.UPDATE) {
this.setSendStream(new ByteArrayInputStream(data));
this.setLength(length);
}
return FileRequest.putRange(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, opContext, accessCondition, range, operationType);
}
@Override
public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
if (operationType == FileRangeOperationType.UPDATE) {
if (options.getUseTransactionalContentMD5()) {
connection.setRequestProperty(Constants.HeaderConstants.CONTENT_MD5, md5);
}
}
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
if (operationType == FileRangeOperationType.UPDATE) {
StorageRequest.signBlobQueueAndFileRequest(connection, client, length, context);
}
else {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
}
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_CREATED) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
return null;
}
};
return putRequest;
}
/**
* Uploads the file's metadata to the storage service.
*
* Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
* from the Microsoft Azure storage service.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void uploadMetadata() throws StorageException, URISyntaxException {
this.uploadMetadata(null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Uploads the file's metadata to the storage service using the access condition, request options, and operation
* context.
*
* Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
* from the Microsoft Azure storage service.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void uploadMetadata(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.uploadMetadataImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
}
private StorageRequest uploadMetadataImpl(final AccessCondition accessCondition,
final FileRequestOptions options) {
final StorageRequest putRequest = new StorageRequest(
options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.setFileMetadata(
file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition);
}
@Override
public void setHeaders(HttpURLConnection connection, CloudFile file, OperationContext context) {
FileRequest.addMetadata(connection, file.metadata, context);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
return null;
}
};
return putRequest;
}
/**
* Updates the file's properties to the storage service.
*
* Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
* from the Microsoft Azure storage service.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void uploadProperties() throws StorageException, URISyntaxException {
this.uploadProperties(null /* accessCondition */, null /* options */, null /*opContext */);
}
/**
* Updates the file's properties using the access condition, request options, and operation context.
*
* Use {@link CloudFile#downloadAttributes} to retrieve the latest values for the file's properties and metadata
* from the Microsoft Azure storage service.
*
* @param accessCondition
* An {@link AccessCondition} object that represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public final void uploadProperties(final AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this,
this.uploadPropertiesImpl(accessCondition, options), options.getRetryPolicyFactory(), opContext);
}
private StorageRequest uploadPropertiesImpl(
final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.setFileProperties(
file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, file.properties);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
return null;
}
};
return putRequest;
}
/**
* Resizes the file to the specified size.
*
* @param size
* A long
which represents the size of the file, in bytes.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
public void resize(long size) throws StorageException, URISyntaxException {
this.resize(size, null /* accessCondition */, null /* options */, null /* operationContext */);
}
/**
* Resizes the file to the specified size.
*
* @param size
* A long
which represents the size of the file, in bytes.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
public void resize(long size, AccessCondition accessCondition, FileRequestOptions options,
OperationContext opContext) throws StorageException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
opContext.initialize();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
ExecutionEngine.executeWithRetry(this.fileServiceClient, this, this.resizeImpl(size, accessCondition, options),
options.getRetryPolicyFactory(), opContext);
}
private StorageRequest resizeImpl(final long size,
final AccessCondition accessCondition, final FileRequestOptions options) {
final StorageRequest putRequest =
new StorageRequest(options, this.getStorageUri()) {
@Override
public HttpURLConnection buildRequest(CloudFileClient client, CloudFile file, OperationContext context)
throws Exception {
return FileRequest.resize(file.getTransformedAddress(context).getUri(this.getCurrentLocation()),
options, context, accessCondition, size);
}
@Override
public void signRequest(HttpURLConnection connection, CloudFileClient client, OperationContext context)
throws Exception {
StorageRequest.signBlobQueueAndFileRequest(connection, client, 0L, context);
}
@Override
public Void preProcessResponse(CloudFile file, CloudFileClient client, OperationContext context)
throws Exception {
if (this.getResult().getStatusCode() != HttpURLConnection.HTTP_OK) {
this.setNonExceptionedRetryableFailure(true);
return null;
}
file.getProperties().setLength(size);
file.updateEtagAndLastModifiedFromResponse(this.getConnection());
this.getResult().setRequestServiceEncrypted(BaseResponse.isServerRequestEncrypted(this.getConnection()));
return null;
}
};
return putRequest;
}
/**
* Uploads the source stream data to the file. If the file already exists on the service, it will be overwritten.
*
* @param sourceStream
* An {@link InputStream} object to read from.
* @param length
* A long
which represents the length, in bytes, of the stream data. Must be non zero.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void upload(final InputStream sourceStream, final long length) throws StorageException, IOException, URISyntaxException {
this.upload(sourceStream, length, null /* accessCondition */, null /* options */, null /* opContext */);
}
/**
* Uploads the source stream data to the file using the specified access condition, request options, and operation
* context. If the file already exists on the service, it will be overwritten.
*
* @param sourceStream
* An {@link InputStream} object to read from.
* @param length
* A long
which represents the length, in bytes, of the stream data. This must be greater than
* or equal to zero.
* @param accessCondition
* An {@link AccessCondition} object which represents the access conditions for the file.
* @param options
* A {@link FileRequestOptions} object that specifies any additional options for the request. Specifying
* null
will use the default request options from the associated service client (
* {@link CloudFileClient}).
* @param opContext
* An {@link OperationContext} object which represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @throws IOException
* If an I/O exception occurred.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
@DoesServiceRequest
public void upload(final InputStream sourceStream, final long length, final AccessCondition accessCondition,
FileRequestOptions options, OperationContext opContext) throws StorageException, IOException, URISyntaxException {
if (opContext == null) {
opContext = new OperationContext();
}
this.getShare().assertNoSnapshot();
options = FileRequestOptions.populateAndApplyDefaults(options, this.fileServiceClient);
if (length < 0) {
throw new IllegalArgumentException(SR.INVALID_FILE_LENGTH);
}
if (sourceStream.markSupported()) {
// Mark sourceStream for current position.
sourceStream.mark(Constants.MAX_MARK_LENGTH);
}
final FileOutputStream streamRef = this.openWriteNew(length, accessCondition, options, opContext);
try {
streamRef.write(sourceStream, length);
}
finally {
streamRef.close();
}
}
/**
* Retrieves the parent name for a file URI.
*
* @param resourceAddress
* A {@link StorageUri} object which represents the resource URI.
* @param delimiter
* A String
which specifies the directory delimiter to use.
* @param usePathStyleUris
* A Boolean
which specifies whether path style URIs are used.
* @param share
* A {@link CloudFileShare} object which represents the file share.
*
* @return A String
which represents the parent address for a file URI.
*
* @throws URISyntaxException
*/
protected static String getParentNameFromURI(final StorageUri resourceAddress, final CloudFileShare share)
throws URISyntaxException {
Utility.assertNotNull("resourceAddress", resourceAddress);
Utility.assertNotNull("share", share);
String delimiter = "/";
String shareName = share.getName() + delimiter;
String relativeURIString = Utility.safeRelativize(share.getStorageUri().getPrimaryUri(),
resourceAddress.getPrimaryUri());
if (relativeURIString.endsWith(delimiter)) {
relativeURIString = relativeURIString.substring(0, relativeURIString.length() - delimiter.length());
}
String parentName;
if (Utility.isNullOrEmpty(relativeURIString)) {
// Case 1 /[Delimiter]*? => /
// Parent of share is share itself
parentName = null;
}
else {
final int lastDelimiterDex = relativeURIString.lastIndexOf(delimiter);
if (lastDelimiterDex < 0) {
// Case 2 //
// Parent of a Directory is Share
parentName = "";
}
else {
// Case 3 ///[/]*
// Parent of CloudFile is CloudFileDirectory
parentName = relativeURIString.substring(0, lastDelimiterDex);
if (parentName != null && parentName.equals(shareName)) {
parentName = "";
}
}
}
return parentName;
}
/**
* Verifies the passed in URI. Then parses it and uses its components to populate this resource's properties.
*
* @param completeUri
* A {@link StorageUri} object which represents the complete URI.
* @param credentials
* A {@link StorageCredentials} object used to authenticate access.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
*/
private void parseQueryAndVerify(final StorageUri completeUri, final StorageCredentials credentials)
throws StorageException, URISyntaxException {
Utility.assertNotNull("completeUri", completeUri);
if (!completeUri.isAbsolute()) {
throw new IllegalArgumentException(String.format(SR.RELATIVE_ADDRESS_NOT_PERMITTED, completeUri.toString()));
}
this.storageUri = PathUtility.stripURIQueryAndFragment(completeUri);
final StorageCredentialsSharedAccessSignature parsedCredentials =
SharedAccessSignatureHelper.parseQuery(completeUri);
if (credentials != null && parsedCredentials != null) {
throw new IllegalArgumentException(SR.MULTIPLE_CREDENTIALS_PROVIDED);
}
try {
final boolean usePathStyleUris = Utility.determinePathStyleFromUri(this.storageUri.getPrimaryUri());
this.fileServiceClient = new CloudFileClient(PathUtility.getServiceClientBaseAddress(
this.getStorageUri(), usePathStyleUris), credentials != null ? credentials : parsedCredentials);
this.name = PathUtility.getFileNameFromURI(this.storageUri.getPrimaryUri(), usePathStyleUris);
}
catch (final URISyntaxException e) {
throw Utility.generateNewUnexpectedStorageException(e);
}
final HashMap queryParameters = PathUtility.parseQueryString(completeUri.getQuery());
final String[] snapshotIDs = queryParameters.get(Constants.QueryConstants.SHARE_SNAPSHOT);
if (snapshotIDs != null && snapshotIDs.length > 0) {
this.getShare().snapshotID = snapshotIDs[0];
}
}
protected void updateEtagAndLastModifiedFromResponse(HttpURLConnection request) {
// ETag
this.getProperties().setEtag(request.getHeaderField(Constants.HeaderConstants.ETAG));
// Last Modified
if (0 != request.getLastModified()) {
final Calendar lastModifiedCalendar = Calendar.getInstance(Utility.LOCALE_US);
lastModifiedCalendar.setTimeZone(Utility.UTC_ZONE);
lastModifiedCalendar.setTime(new Date(request.getLastModified()));
this.getProperties().setLastModified(lastModifiedCalendar.getTime());
}
}
protected void updateLengthFromResponse(HttpURLConnection request) {
final String xContentLengthHeader = request.getHeaderField(FileConstants.CONTENT_LENGTH_HEADER);
if (!Utility.isNullOrEmpty(xContentLengthHeader)) {
this.getProperties().setLength(Long.parseLong(xContentLengthHeader));
}
}
/**
* Returns the file's share.
*
* @return A {@link CloudFileShare} object that represents the share of the file.
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
@Override
public final CloudFileShare getShare() throws StorageException, URISyntaxException {
if (this.share == null) {
final StorageUri shareUri = PathUtility.getShareURI(this.getStorageUri(),
this.fileServiceClient.isUsePathStyleUris());
this.share = new CloudFileShare(shareUri, this.fileServiceClient.getCredentials()/*, null*/);
}
return this.share;
}
/**
* Returns the metadata for the file.
*
* @return A java.util.HashMap
object that represents the metadata for the file.
*/
public final HashMap getMetadata() {
return this.metadata;
}
/**
* Returns the name of the file.
*
* @return A String
that represents the name of the file.
*/
public final String getName() {
return this.name;
}
/**
* Returns the file item's parent.
*
* @return A {@link CloudFileDirectory} object that represents the parent directory for the file.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
@Override
public final CloudFileDirectory getParent() throws URISyntaxException, StorageException {
if (this.parent == null) {
final String parentName = getParentNameFromURI(this.getStorageUri(), this.getShare());
if (parentName != null) {
StorageUri parentURI = PathUtility.appendPathToUri(this.share.getStorageUri(), parentName);
this.parent = new CloudFileDirectory(parentURI, this.getServiceClient().getCredentials());
}
}
return this.parent;
}
/**
* Returns the file's properties.
*
* @return A {@link FileProperties} object that represents the properties of the file.
*/
public final FileProperties getProperties() {
return this.properties;
}
/**
* Returns the file service client associated with the file.
*
* @return A {@link CloudFileClient} object that represents the client.
*/
public final CloudFileClient getServiceClient() {
return this.fileServiceClient;
}
/**
* Gets the number of bytes to buffer when writing to a {@link FileOutputStream}.
*
* @return
* A int
which represents the number of bytes to buffer.
*/
public final int getStreamWriteSizeInBytes() {
return this.streamWriteSizeInBytes;
}
/**
* Returns the minimum read size when using a {@link FileInputStream}.
*
* @return A int
which represents the minimum read size, in bytes, when using a {@link FileInputStream}
* object.
*/
public final int getStreamMinimumReadSizeInBytes() {
return this.streamMinimumReadSizeInBytes;
}
/**
* Returns the list of URIs for all locations.
*
* @return A {@link StorageUri} that represents the list of URIs for all locations.
*/
@Override
public final StorageUri getStorageUri() {
return this.storageUri;
}
/**
* Returns the URI for this file.
*
* @return A java.net.URI
object that represents the URI for the file.
*/
@Override
public final URI getUri() {
return this.storageUri.getPrimaryUri();
}
/**
* Sets the share for the file.
*
* @param share
* A {@link CloudFileShare} object that represents the share being assigned to the file.
*/
protected final void setShare(final CloudFileShare share) {
this.share = share;
}
/**
* Sets the metadata for the file.
*
* @param metadata
* A java.util.HashMap
object that contains the metadata being assigned to the file.
*/
public final void setMetadata(final HashMap metadata) {
this.metadata = metadata;
}
/**
* Sets the properties for the file.
*
* @param properties
* A {@link FileProperties} object that represents the properties being assigned to the file.
*/
protected final void setProperties(final FileProperties properties) {
this.properties = properties;
}
/**
* Sets the list of URIs for all locations.
*
* @param storageUri
* A {@link StorageUri} that represents the list of URIs for all locations.
*/
protected void setStorageUri(final StorageUri storageUri) {
this.storageUri = storageUri;
}
/**
* Sets the minimum read size when using a {@link FileInputStream}.
*
* @param minimumReadSize
* An int
that represents the minimum number of bytes to buffer when reading from
* a file while using a {@link FileInputStream} object. Must be greater than or equal to 16 KB.
* @throws IllegalArgumentException
* If minimumReadSize
is less than 16 KB.
*/
public void setStreamMinimumReadSizeInBytes(final int minimumReadSize) {
if (minimumReadSize < 16 * Constants.KB) {
throw new IllegalArgumentException("MinimumReadSize");
}
this.streamMinimumReadSizeInBytes = minimumReadSize;
}
/**
* Sets the number of bytes to buffer when writing to a {@link FileOutputStream}.
*
* @param streamWriteSizeInBytes
* An int
which represents the number of bytes to buffer while using a
* {@link FileOutputStream} object, ranging from 512 bytes to 4 MB, inclusive.
*
* @throws IllegalArgumentException
* If streamWriteSizeInBytes
is less than 512 bytes or greater than 4 MB.
*/
public void setStreamWriteSizeInBytes(final int streamWriteSizeInBytes) {
if (streamWriteSizeInBytes > Constants.MAX_FILE_WRITE_SIZE || streamWriteSizeInBytes < Constants.MIN_PERMITTED_FILE_WRITE_SIZE) {
throw new IllegalArgumentException("StreamWriteSizeInBytes");
}
this.streamWriteSizeInBytes = streamWriteSizeInBytes;
}
/**
* Returns the transformed URI for the resource if the given credentials require transformation.
*
* @param opContext
* An {@link OperationContext} object that represents the context for the current operation. This object
* is used to track requests to the storage service, and to provide additional runtime information about
* the operation.
*
* @return A {@link StorageUri} object that represents the transformed URI.
*
* @throws StorageException
* If a storage service error occurred.
* @throws URISyntaxException
* If the resource URI is invalid.
*/
protected final StorageUri getTransformedAddress(final OperationContext opContext)
throws URISyntaxException, StorageException {
return this.fileServiceClient.getCredentials().transformUri(this.getStorageUri(), opContext);
}
}