Java에서 Azrue Storage 라이브러리를 이용해, Azure의 BlobStorage로 파일을 업로드, 다운로드, File Share로 복사하는 기능을 만들었다.
Upload
@RequiredArgsConstructor
@RestController
@RequestMapping("/blobFile")
public class BlobController {
private final AzureBlobService azureBlobService;
@PostMapping
public ResponseEntity<String> uploadBlobFile(
StandardMultipartHttpServletRequest multiRequest) throws Exception {
return ResponseEntity.ok().body(azureBlobService.uploadBlobFile(multiRequest));
}
}
@RequiredArgsConstructor
@Service
public class AzureBlobService {
//@Value는 application.properties에 설정되어있는 값이다.
//Azure의 Account Name과 Key가 있는 Connection String.
@Value("${azure.storage.connectionString}")
private String connectionString;
public String uploadBlobFile(StandardMultipartHttpServletRequest multiRequest)
throws Exception {
MultipartFile file = multiRequest.getFiles("file").get(0);
String fileName = file.getOriginalFilename();
@Cleanup InputStream is = new ByteArrayInputStream(file.getBytes());
CloudStorageAccount account = CloudStorageAccount.parse(connectionString);
CloudBlobClient client = account.createCloudBlobClient();
String containerName = "BlobConatiner";
CloudBlobContainer container = client.getContainerReference(containerName);
container.createIfNotExists();
CloudBlockBlob blockBlob = container.getBlockBlobReference(fileName);
byte[] buffer = ByteStreams.toByteArray(is);
blockBlob.uploadFromByteArray(buffer, 0, buffer.length);
is.close();
return fileName;
}
}
Dowonload
@RequiredArgsConstructor
@RestController
@RequestMapping("/blobFile")
public class BlobController {
private final AzureBlobService azureBlobService;
@GetMapping
public void downloadBlobFile(
HttpServletRequest request,
HttpServletResponse response,
@RequestParam(name = "blobFileName") String blobFileName) throws Exception {
String userAgent = request.getHeader("User-Agent");
azureBlobService.downloadBlobFile(blobFileName, userAgent, response);
}
}
@RequiredArgsConstructor
@Service
public class AzureBlobService {
//@Value는 application.properties에 설정되어있는 값이다.
//Azure의 Account Name과 Key가 있는 Connection String.
@Value("${azure.storage.connectionString}")
private String connectionString;
public void downloadBlobFile(String blobFileName, String userAgent, HttpServletResponse response)
throws Exception {
String fileExetension = blobFileName.substring(blobFileName.lastIndexOf(".") + 1);
String contentType = getContentType(fileExetension);
if (StringUtils.hasText(contentType)) {
response.setContentType(contentType);
}
String attachHeader = getAttachHeader(userAgent, blobFileName);
if (StringUtils.hasText(attachHeader)) {
response.setHeader("Content-Disposition", attachHeader);
}
response.setHeader("Content-Transfer-Encoding", "binary");
CloudStorageAccount account = CloudStorageAccount.parse(connectionString);
CloudBlobClient client = account.createCloudBlobClient();
String containerName = "BlobConatiner";
CloudBlobContainer container = client.getContainerReference(containerName);
CloudBlockBlob blockBlob = container.getBlockBlobReference(blobFileName);
@Cleanup ByteArrayOutputStream os = new ByteArrayOutputStream();
blockBlob.download(os);
@Cleanup InputStream is = new ByteArrayInputStream(os.toByteArray());
IOUtils.copy(is, response.getOutputStream());
response.flushBuffer();
}
//response의 ContentType 리턴
public static String getContentType(String fileExtension) {
String contentType = "application";
if (fileExtension.trim().length() > 0)
contentType += "/" + fileExtension;
if (fileExtension.equalsIgnoreCase("htm") || fileExtension.equalsIgnoreCase("html"))
contentType = "text/HTML";
else if (fileExtension.equalsIgnoreCase("txt"))
contentType = "text/plainL";
else if (fileExtension.equalsIgnoreCase("doc") || fileExtension.equalsIgnoreCase("rtf")
|| fileExtension.equalsIgnoreCase("docx"))
contentType = "Application/msword";
else if (fileExtension.equalsIgnoreCase("xls") || fileExtension.equalsIgnoreCase("xlsx"))
contentType = "Application/x-msexcel";
else if (fileExtension.equalsIgnoreCase("jpg") || fileExtension.equalsIgnoreCase("jpeg"))
contentType = "image/jpeg";
else if (fileExtension.equalsIgnoreCase("gif"))
contentType = "image/GIF";
else if (fileExtension.equalsIgnoreCase("pdf"))
contentType = "application/pdf";
return contentType;
}
//첨부 파일의 헤더 정보 리턴
public static String getAttachHeader(String userAgent, String fileName)
throws UnsupportedEncodingException {
String attachHeader = "";
//agent는 클라이언트의 브라우저 정보이다.
String agent = StringUtil.isEmpty(userAgent) ? "" : userAgent.toUpperCase();
if (agent.contains("TRIDENT") || agent.contains("MSIE") || agent.contains("EDGE")) {
String cnvrtFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
attachHeader = "attachment;filename=" + cnvrtFileName + ";";
} else {
String cnvrtFileName = new String(fileName.getBytes("UTF-8"), "ISO-8859-1");
attachHeader = "attachment; filename=\"" + cnvrtFileName + "\"";
}
return attachHeader;
}
}
하나의 BlobStorage에서 다른 BlobStorage로 Blob파일 복사
CloudBlockBlob을 이용해 복사한다. CloudBlockBlob 클래스를 사용하기 위해서는 pom.xml에 azure-storage 말고도, azure-storage-blob 디펜던시를 추가해야 한다.
<!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-storage -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
<version>8.6.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.microsoft.azure/azure-storage-blob -->
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>11.0.1</version>
</dependency>
CloudBlobContainer와 CloudBlockBlob을 이용해 아래와 같이 복사하면 된다.
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.blob.CloudBlobClient;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class BlobCopyService {
@Value("${azure.storage.connectionString}")
private String connectionString;
public void copyFile(String blobFileName, String fromBlobContainerName, String toBlobContainerName) throws Exception {
CloudBlobContainer fromContainer = getBlobContainer(fromBlobContainerName);
CloudBlobContainer toContainer = getBlobContainer(toBlobContainerName);
CloudBlockBlob fromBlockBlob = fromContainer.getBlockBlobReference(blobFileName);
CloudBlockBlob toBlockBlob = toContainer.getBlockBlobReference(blobFileName);
toBlockBlob.startCopy(fromBlockBlob);
}
public CloudBlobContainer getBlobContainer(String containerName) throws Exception {
CloudStorageAccount account = CloudStorageAccount.parse(connectionString);
CloudBlobClient client = account.createCloudBlobClient();
return client.getContainerReference(containerName);
}
}
BlobStorage에서 FileShares로 Copy
FileShares로 Copy할 때는 Container의 공용 액세스 수준이 Private이면 컨테이너의 SAS(공유 액세스 서명) 토큰이 필요하다. 공용 액세스 수준이 컨테이너면 SAS가 필요하지 않다.
공용 액세스 수준이 컨테이너 인 경우 (SAS 토큰이 필요없는 경우)
@Slf4j
@Service
public class AzureBlobService {
@Value("${azure.storage.connectionString}")
private String connectionString;
private static final String TEMP_DIRECTORY_PATH = "temp/";
public void copyFileWithoutSAS(String blobFileName) throws Exception {
CloudStorageAccount account = CloudStorageAccount.parse(connectionString);
CloudBlobClient client = account.createCloudBlobClient();
String containerName = "BlobConatiner";
CloudBlobContainer container = client.getContainerReference(containerName);
//CloudFile에 복사하고자 하는 BlobFile을 가지고 온다.
CloudBlockBlob blockBlob = container.getBlockBlobReference(blobFileName);
Sring fileShareName = "fileShare";
CloudFileShare fileShare = AzureFileHandler.getFileShare(connectionString, fileShareName);
CloudFileDirectory rootDir = fileShare.getRootDirectoryReference();
CloudFileDirectory tempDir = rootDir.getDirectoryReference(TEMP_DIRECTORY_PATH);
String newFileName = blobFileName;
CloudFile cloudFile = tempDir.getFileReference(newFileName);
cloudFile.startCopy(blockBlob);
}
}
공용 액세스 수준이 프라이빗 한 경우 (SAS 토큰이 필요한 경우)
공유 액세스 토큰 생성해서 사용하는 방법
사용하고자 하는 컨테이너의 공유 액세스 토큰에 가서 Blob SAS 토큰을 생성하고 복사해줍니다.
SAS 토큰 및 URL 생성 누를 때마다 새로운 토큰이 생성됩니다.
만료일은 2999년으로하고 생성을 하였습니다.
전 @Valid로 application.properties에서 토큰값을 가져와서 사용할 수 있도록
복사한 토큰을 application.properties에 넣어줬습니다.
SAS 토큰을 사용하지 않고 copy하려는 경우 the specified resource does not exist. 라는 에러 메시지가 나옵니다.
@Slf4j
@Service
public class AzureBlobService {
@Value("${azure.storage.connectionString}")
private String connectionString;
@Value("${azure.storage.blob.container.sasToken}")
private String sasToken;
private static final String TEMP_DIRECTORY_PATH = "temp/";
public void copyFileWithoutSAS(String blobFileName) throws Exception {
CloudStorageAccount account = CloudStorageAccount.parse(connectionString);
CloudBlobClient client = account.createCloudBlobClient();
String containerName = "BlobConatiner";
CloudBlobContainer container = client.getContainerReference(containerName);
//CloudFile에 복사하고자 하는 BlobFile을 가지고 온다.
CloudBlockBlob blockBlob = container.getBlockBlobReference(blobFileName);
Sring fileShareName = "fileShare";
CloudFileShare fileShare = AzureFileHandler.getFileShare(connectionString, fileShareName);
CloudFileDirectory rootDir = fileShare.getRootDirectoryReference();
CloudFileDirectory tempDir = rootDir.getDirectoryReference(TEMP_DIRECTORY_PATH);
String newFileName = blobFileName;
CloudFile cloudFile = tempDir.getFileReference(newFileName);
//SAS 토큰을 설정해준다.
StorageCredentialsSharedAccessSignature sasCredentials =
new StorageCredentialsSharedAccessSignature(sasToken);
cloudFile.startCopy(sasCredentials.transformUri(blockBlob.getUri()));
}
}
액세스 정책을 이용해 토큰을 생성하는 방법
사용하고자 하는 컨테이너의 액세스 정책에 가서 정책을 만들어줍니다.
@Slf4j
@Service
public class AzureBlobService {
@Value("${azure.storage.connectionString}")
private String connectionString;
private static final String TEMP_DIRECTORY_PATH = "temp/";
public void copyFileWithoutSAS(String blobFileName) throws Exception {
CloudStorageAccount account = CloudStorageAccount.parse(connectionString);
CloudBlobClient client = account.createCloudBlobClient();
String containerName = "BlobConatiner";
CloudBlobContainer container = client.getContainerReference(containerName);
//CloudFile에 복사하고자 하는 BlobFile을 가지고 온다.
CloudBlockBlob blockBlob = container.getBlockBlobReference(blobFileName);
Sring fileShareName = "fileShare";
CloudFileShare fileShare = AzureFileHandler.getFileShare(connectionString, fileShareName);
CloudFileDirectory rootDir = fileShare.getRootDirectoryReference();
CloudFileDirectory tempDir = rootDir.getDirectoryReference(TEMP_DIRECTORY_PATH);
String newFileName = blobFileName;
CloudFile cloudFile = tempDir.getFileReference(newFileName);
//액세스 정책을 가져와 SAS 토큰을 생성한다.
SharedAccessBlobPolicy policy =
blockBlob.getContainer().downloadPermissions().getSharedAccessPolicies().get("my-policy");
String sasToken = blockBlob.generateSharedAccessSignature(policy, null, null);
//SAS 토큰을 설정해준다.
StorageCredentialsSharedAccessSignature sasCredentials =
new StorageCredentialsSharedAccessSignature(sasToken);
cloudFile.startCopy(sasCredentials.transformUri(blockBlob.getUri()));
}
}
'Cloud > Azure' 카테고리의 다른 글
Local에서 Docker Image 만든 후, Azure 서버에 K8S로 배포하기 (0) | 2022.03.21 |
---|---|
Azure CLI(명령줄 인터페이스) 설치 및 K8S 자격증명 받기 (0) | 2022.03.21 |
Azure DevOps에서 SSH 키 설정하고 Git 프로젝트 Clon하기 (0) | 2022.03.11 |
BlobStorage의 LifeCycle(수명주기) 설정 방법 (0) | 2022.03.11 |
Azure 공유 파일(File Shares) Properties 정보 가져오기 (0) | 2022.03.04 |