www.dextsolution.com
DEXTUPLOAD
X5
menu toggle제품설명 > 압축 다운로드하기 (ASP.NET)

압축 다운로드하기 (ASP.NET)

DEXTUploadX5 1.3.0.0 버전부터 압축 다운로드 기능을 지원한다. 압축 다운로드는 다운로드할 대상들을 하나의 압축 파일로 묶어서 다운로드하는 방식을 말한다.

한 가지 주의할 점은 파일을 압축하는 역할은 서버가 담당을 하는 것이며, DEXTUploadX5 제품에서 파일을 압축하는 기능을 가지고 있는 것은 아니다. DEXTUploadX5는 다운로드할 대상의 vindex 속성값을 서버로 전달한다. 서버는 vindex 값을 가지고 압축할 대상을 선정하여 하나의 압축 파일을 생성하게 된다. 압축 파일이 생성되면, 그 파일을 다운로드 받을 수 있는 경로를 클라이언트로 다시 반환하고, DEXTUploadX5가 이 다운로드 경로를 받아 다시 호출하는 방식으로 구현되어 있다.

서버는 파일을 압축할 수 있는 기능을 가지고 있어야 하는데, 여러 압축 라이브러리를 써서 기능을 구현할 수 있지만, ASP.NET 환경이라면 .NET Framework 4.5부터 지원하는 ZipArchive 클래스를 사용하여 파일을 압축할 수 있다.

다운로드 항목 등록하기

DEXTUploadX5는 다운로드할 항목을 반드시 가상 파일로 등록해야 한다.

 

가상 파일이란 사용자 로컬 PC에 존재하지 않으며, 실체가 없는 가상의 파일을 말한다. 즉 실제로 로컬에 존재하지 않는 파일이므로, 파일 업로드 대상이 아니다. 일반적으로 가상 파일은 이미 업로드한 파일에 대한 정보를 남기는 용도로 사용하곤 한다. (서버에 존재하는 파일이라는 표시)

 

가상 파일을 등록하려면 addVirtualFile 함수를 사용하거나 addVirtualFileList 함수를 사용하며, json 객체 형식으로 등록이 된다.

  • vindex: 가상 파일을 구분 짓는 유일한 키로써, 어떤 형식이라도 상관이 없지만, 중복되지 않아야 한다. (필수항목)
  • name: 가상 파일의 이름이다. (필수항목)
  • size: 가상 파일의 크기이며 byte 단위를 사용한다. (필수항목)
  • lock: 잠금 상태가 true이면 파일을 삭제할 수 없다.
var dx = dx5.get(id);
// 개별로 등록할 때
dx.addVirtualFile({ vindex: "IDX0001", name: "가상파일.txt", size: 12345 });
dx.addVirtualFile({ vindex: "IDX0002", name: "잠긴-가상파일.txt", size: 45678, lock: true });
dx.addVirtualFile({ vindex: "IDX0003", name: "코스모스.jpg", size: 195779 });

// 여러 개를 한 번에 등록할 때
dx.addVirtualFileList([
  { vindex: "IDX0001", name: "가상파일.txt", size: 12345 },
  { vindex: "IDX0002", name: "잠긴-가상파일.txt", size: 45678, lock: true },
  { vindex: "IDX0003", name: "코스모스.jpg", size: 195779 }
]);

단일/다중 파일 다운로드와 달리 압축 다운로드를 사용할 때에는 url(또는 downUrl) 속성을 설정할 필요가 없다. 압축 다운로드는 파일 압축하여 그 다운로드 경로를 반환하는 역할을 담당하는 주소만 필요한다. 그러므로 각각의 가상 파일마다 설정했던 url(또는 downUrl) 속성은 압축 다운로드에서는 아무런 영향을 미치지 않는다.

압축 경로 설정하기

다운로드 받을 대상을 가상 파일로 등록하는 작업이 끝나면, 압축을 담당하는 경로를 설정해주어야 한다. 압축 경로는 setCompressURL 메소드를 사용하여 한 번만 등록하면 된다.

경로는 반드시 스키마(http, https)로 시작하는 웹 URL이어야 한다.

function onDX5Created(id) {
  var dx = dx5.get(id);
    
  dx.addVirtualFile({ vindex: "IDX0001", name: "가상파일.txt", size: 12345 });
  ...
  dx.addVirtualFile({ vindex: "IDX0005", name: "코스모스 (빈공간) 195779.jpg", size: 195779 });

  // 압축을 처리할 경로를 설정한다.
  dx.setCompressURL("http://domain/path/compress.ashx");
}
버튼 연결하기

가상 파일과 압축 다운로드 경로 설정이 완료되었으면, downloadCompressed 메소드를 사용하여 압축 다운로드를 시작하면 된다.

<button type="button" onclick="compress('component-id');">압축 다운로드</button>
<script>
function compress(id) {
  // 플래그 값에 따라 대상을 달리하여 압축 다운로드를 수행한다.
  // AUTO: 전체 가상 파일을 압축 다운로드한다.
  // SELECTED: 선택된 가상 파일을 압축 다운로드한다.
  // CHECKED: 체크된 가상 파일을 압축 다운로드한다.
  dx5.get(id).downloadCompressed("AUTO");
}
</script>

복잡한 스크립트를 사용하지 않고서 컴포넌트 로드 시점에 자동 바인딩이 되는 기능도 함께 제공하고 있다.

<button type="button" id="btn-compress-auto">압축 다운로드</button>
<button type="button" id="btn-compress-selected">선택 압축 다운로드</button>
<button type="button" id="btn-compress-checked">체크 압축 다운로드</button>
<script>
  dx5.create({
    ...,
    // 컴포넌트가 생성될 때, 압축 다운로드 기능이 자동으로 연결하도록 한다.
    btnDownloadCompressedAuto: "btn-compress-auto", 
    btnDownloadCompressedSelected: "btn-compress-selected", 
    btnDownloadCompressedChecked: "btn-compress-checked"
  });
</script>

자동 바인딩 기능은 매우 편리하나, 복잡한 기능을 구현하기에는 적합치 않을 수 있다.

서버측 처리 (ASP.NET)

compress.ashx에서 ZipArchive 클래스를 사용하여 압축 작업을 처리할 수 있다.

DEXTUploadX5는 downloadCompressed 메소드를 호출하면 vindex 속성값을 콤마(,) 문자를 구분자로 하는 목록 문자열을 생성하고, 이 값을 'DEXTUploadX5_VIndexes' 이름이 붙혀진 폼 데이터를 POST 형식으로 submit 한다.

// DEXTUploadX5_VIndexes 폼 이름으로부터 압축할 대상의 가상 인덱스 집합을 얻을 수 있다.
var data = context.Request.Form["DEXTUploadX5_VIndexes"] ?? string.Empty;
var vindexes = data.Split(',');
var targets = new List<string>();

foreach (string index in vindexes)
{
    if (index.Equals("IDX0003")) targets.Add(context.Server.MapPath("~/files/attach/서강대교_509147.jpg"));
    else if (index.Equals("IDX0004")) targets.Add(context.Server.MapPath("~/files/attach/우도해변_239826.jpg"));
    else if (index.Equals("IDX0005")) targets.Add(context.Server.MapPath("~/files/attach/코스모스 (빈공간) 195779.jpg"));
}

var zipPath = Path.GetTempFileName();

// .NET Framework 4.5 부터 지원하는 ZipArchive를 이용하여 압축 파일을 생성한다.
using (var fs = new FileStream(zipPath, FileMode.Create))
using (var za = new ZipArchive(fs, ZipArchiveMode.Create, false, System.Text.Encoding.UTF8))
{
    foreach (string path in targets)
    {
        za.CreateEntryFromFile(path, Path.GetFileName(path));
    }
}

// 예제가 아닌 실제 상황에서는 세션 또는 데이터베이스와 같은 다른 저장 방식을 이용하여
// 실제로 다운로드를 수행해야 하는 핸들러(zip-download.ashx)에서 압축 파일의 경로를 얻을 수 있도록 처리해야 한다.
// FileRepository 및 FileEntity 클래스는 샘플 구성과 이해를 돕기 위해서 작성되었다.

FileRepository.Clear();
var key = FileRepository.Add(new FileEntity {
    Path = zipPath,
    MimeType = "application/x-zip-compressed",
    FileName = string.Concat(Path.GetFileNameWithoutExtension(zipPath), ".zip")
});
            
// 압축된 파일을 다운로드할 수 있는 핸들러 경로를 생성한다.
UriBuilder b = new UriBuilder(context.Request.Url);            
b.Path = VirtualPathUtility.ToAbsolute("~/download/zip-download.ashx");
b.Query = string.Concat("key=", key);

// 압축된 파일을 다운로드할 수 있는 경로를 응답 데이터에 실어 클라이언트에 전달해야 한다.
context.Response.ContentType = "text/plain";
context.Response.Write(b.ToString());

대상 파일들을 압축하고 난 후, 압축 파일을 다운로드해야 한다. 앞서 응답 데이터에 기록된 값이 바로 압축 다운로드를 다운로드할 수 있는 서버 측 경로이며 이는 다음과 같은 방식으로 구현할 수 있다.

var key = context.Request.QueryString["key"] ?? string.Empty;
var entry = FileRepository.Get(key);

if (entry != null)
{
    // DEXTUpload.NET Professional 제품을 사용하여 다운로드를 수행한다.
    using (var dext = new FileDownload())
    {
        dext.Download(entry.Path, entry.FileName, new DownloadOption {
            // 다운로드를 한 후 압축 파일은 무의미한 파일이므로 삭제하도록 한다.
            RemoveAfterDownloading = true,
            // 압축 파일은 매 요청마다 생성되므로 클라이언트 캐시를 사용하지 않도록 한다.
            UseClientCache = false,
            // application/x-zip-compressed
            MimeType = entry.MimeType
        });
    }
}
else
{
    throw new HttpException(404, "The zip file not found.");
}
서버측 처리 (ASP.NET MVC)

다음의 주소로 압축 작업을 요청한다고 가정한다.

function onDX5Created(id) {
  var dx = dx5.get(id);
    
  dx.addVirtualFile({ vindex: "IDX0001", name: "가상파일.txt", size: 12345 });
  ...
  dx.addVirtualFile({ vindex: "IDX0005", name: "코스모스 (빈공간) 195779.jpg", size: 195779 });
  // 압축을 처리할 경로를 설정한다.
  dx.setCompressURL("http://domain/DownloadSample/Compress");
}

서버는 DownloadSampelController의 Compress 메소드에 ZipArchive 클래스를 사용하여 압축 작업을 처리한다.

[HttpPost]
public ActionResult Compress([Bind(Prefix = "DEXTUploadX5_VIndexes")] string data)
{
    var vindexes = data.Split(',');
    var targets = new List<string>();

    foreach (string index in vindexes)
    {
        if (index.Equals("IDX0003")) targets.Add(Server.MapPath("~/files/attach/서강대교_509147.jpg"));
        else if (index.Equals("IDX0004")) targets.Add(Server.MapPath("~/files/attach/우도해변_239826.jpg"));
        else if (index.Equals("IDX0005")) targets.Add(Server.MapPath("~/files/attach/코스모스 (빈공간) 195779.jpg"));
    }

    var zipPath = Path.GetTempFileName();

    // .NET Framework 4.5 부터 지원하는 ZipArchive를 이용하여 압축 파일을 생성한다.
    using (var fs = new FileStream(zipPath, FileMode.Create))
    using (var za = new ZipArchive(fs, ZipArchiveMode.Create, false, System.Text.Encoding.UTF8))
    {
        foreach (string path in targets)
        {
            za.CreateEntryFromFile(path, Path.GetFileName(path));
        }
    }

    // 예제가 아닌 실제 상황에서는 세션 또는 데이터베이스와 같은 다른 저장 방식을 이용하여
    // 실제로 다운로드를 수행해야 하는 경로에서 압축 파일의 경로를 얻을 수 있도록 처리해야 한다.

    ...

    // 압축된 파일을 다운로드할 수 있는 경로를 응답 데이터에 실어 클라이언트에 전달해야 한다.
    return Content(Url.Action("DownloadZip", new { id = key }), "text/plain", Encoding.UTF8);
}

대상 파일들을 압축하고 난 후, 압축 파일을 다운로드해야 한다. 앞서 응답 데이터에 기록된 값이 바로 압축 다운로드를 다운로드할 수 있는 서버 측 경로이며 이는 다음과 같은 방식으로 구현할 수 있다.

[HttpGet]
public ActionResult DownloadZip(string id)
{
    var entry = FileRepository.Get(id);

    if (entry == null)
    {
        throw new HttpException(404, "The zip file not found.");
    }
    else
    {
        // 다운로드를 한 후 압축 파일을 삭제하는 기능은 제공하지 않는다.
        return File(entry.Path, "application/x-zip-compressed", entry.FileName);
    }
}
압축 다운로드의 제한 및 주의할 점
  • DEXTUploadX5는 압축 다운로드를 하기위한 일련의 작업 과정을 지원하는 것으로, 자체적으로 파일을 압축하는 기능을 가지고 있지 않는다.
  • 압축 다운로드는 서버 측에서 파일을 압축하는 과정이 존재한다. 파일을 압축하는데 필요로 하는 시간이 길어질 경우 의도치 않은 문제가 발생할 수 있다.

    파일을 압축하는 작업은 서버의 리소스(CPU, I/O)를 상당히 소모하는 작업이기 때문에, 크기가 큰 파일 또는 압축 파일의 대상이 되는 파일의 개수가 많은 경우, 서버에 상당한 오버헤드가 발생하므로, 상황에 따라 파일 압축 과정을 기다리는 시간이 길어질 수 있으며, 이는 세션이 끊기는 문제가 초래하거나, 서비스 자체가 중지되는 중대한 문제로 이어질 수 있다. 따라서 압축 다운로드을 하기 위한 정책적 제한을 수립 후, 기능을 사용하는 것을 권장한다.

  • 압축이 서버에서 진행되는 동안 DEXTUploadX5는 진행창이 표시되지만, 압축된 파일을 다운로드할 때는 단일 파일 다운로드로 전환되기 때문에 다운로드 과정에 대한 자체 진행창은 제공되지 않는다.
  • macOS Safari 브라우저에서 압축 다운로드를 수행하면, 브라우저의 팝업 차단 기능으로 인하여 다운로드 과정이 원할하지 않을 수 있다. 그러므로 서비스하고자 하는 사이트의 팝업을 차단하는 Safari 설정을 미리 해지할 필요가 있다.

    macOS Safari 브라우저는 파일을 다운로드한 후 '안전한 파일 열기' 기능에 의해 zip 파일을 풀어버리기 때문에, zip 파일 그대로 다운로드가 되지 않을 수 있다. 만약 zip 파일이 자동으로 풀리는 현상을 막고자 한다면, Safari 환경 설정의 일반 탭에서 해당 설정을 체크 해지하면 된다.