본문 바로가기

Cloud/Azure

Blob Storage에 Blob Upload/Downalod/Copy, FileShare로 복사

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()));

    }
   }