개발팁2018. 5. 10. 17:38

닷넷에서 데이터를 캐시하기위해서는


1. System.Web 의 Cache 를 이용하거나,

2. System.Runtime 의 ObjectCache 를 이용하여, 캐시 데이터를 저장할 수 있다.


Cache 를 생성할때는, 만료시간(Expiration Time)과 만료방식을 2가지로 설정해줄 수 있다.


1. Absolute Expiration

2. Sliding Expiration


Absolute Expiration 은 지정된 시간이 되면, 무조건 캐시 데이터가 만료되고,

Sliding Expiration 은 지정된 시간이 되면, 무조건 만료되는것은 동일하지만, 캐시 데이터를 읽기(Get)를 하게 되면, 만료시간이 다시 늘어나는 방식이다. (로그인 세션을 생각하면 이해가 쉬울것이다.)



클라이언트(웹브라우저)에 데이터를 캐시하기 위해서 쿠키(Cookie) 를 사용할 수도 있지만, 이는 굉장히 비효율적이다.

서버로 Request 를 할때마다 불필요한 트래픽이 발생하며, 저장용량도 4K 정도로 매우 작다.


그래서, 브라우저 내에서 필요한 캐시를 하기 위해, LocalStorage 또는 SessionStorage 를 사용할 것이다.


LocalStorage 은 디스크내에 저장이 되며, 브라우저에 따라 5M ~ 10M 정도의 저장공간을 제공하고, 브라우저가 종료되도 데이터는 유지가 된다.

SessionStorage 는 메모리 내에 존재하며, 브라우저 세션이 종료 (브라우저가 종료) 되면, 데이터도 삭제된다.


LocalStorage / SessionStorage 는 별도의 Expiration Time 기능을 제공하지 않고,

getItem / setItem / removeItem 등의 함수만 제공이 된다.


이를 활용하여, Expiration Time 과 Expiration 방법도 함께 적용해 볼것이다.



// -----------------------------------------------------------------------------------------------------

// Cache Base Object

// Author : bemeal2@naver.com

// do not remove any comments

// -----------------------------------------------------------------------------------------------------

window.cache = {

    get: function (key, isSlidingExpiration, expirationHour) {

        let jsonString = window.sessionStorage && window.sessionStorage.getItem && window.sessionStorage.getItem(key);

        if (jsonString) {

            let cacheData = JSON.parse(jsonString);

            let expirationDate = new Date(cacheData.expirationDate);

            if (expirationDate > new Date()) { // 만료시간 체크 : 지났으면 삭제

let cacheDataObject = JSON.parse(cacheData.value);

if(isSlidingExpiration) // Sliding Expiration 이라면

{

this.set(key, cacheDataObject, expirationHour); // 만료시간을 연장하기 위해, Cache Data 를 재설정한다.

}

                return cacheDataObject;

            } else {

                this.remove(key);

            }

        }

        return null;

    },

    set: function(key, value, expirationHour) {

        let expirationDate = new Date(new Date().getTime() + (60 * 60000 * expirationHour))

        let cacheData = {

            value: JSON.stringify(value),

            expirationDate: expirationDate.toISOString()

        }

        window.sessionStorage && window.sessionStorage.setItem && window.sessionStorage.setItem(key, JSON.stringify(cacheData))

    },

    remove: function (key) {

        window.sessionStorage && window.sessionStorage.removeItem && window.sessionStorage.removeItem(key);

    }

}; 



우선 Base 객체를 하나 생성한다.

이름은 window.cache 이며, get / set / remove 3개의 함수만으로 이루어져 있다.


window.sessionStorage 를 사용하고 있지만, localStorage 를 사용하고자 한다면, 


window.sessionStorage => window.localStorage 로 바꾸기만 하면 된다.



원리는 간단하다.


1. 저장할때는 sessionStorage 에 JSON 객체를 stringify 로 문자열 형태로 바꿔서 저장하고,

    읽어올때는 JSON.parse 로 해서, 다시 객체 형태로 읽어온다.

2. 이때, 만료시간을 지정하기 위해, cacheData 라는 json 객체로 감싸서, value / expirationDate 로 저장하고 있다.

3. 또한, 가져오기(get) 할때, sliding 캐쉬일경우에는 만료시간을 늘려주기 위해 다시  set 함수를 호출하고 있다.



이 window.cache 객체 그대로도 사용할 수 있지만, 매번 함수안에 파라미터를 넣어줘야 한다. 이를 상속하여, 단순화 시켜 보자.


// -----------------------------------------------------------------------------------------------------

// Object Cache Data

// Author : bemeal2@naver.com

// do not remove any comments

// -----------------------------------------------------------------------------------------------------

window.cache.data = Object.create(window.cache, {

    cacheData: { value: "data", enumerable: false, writable: false },

isSlidingExpiration: { value: false, enumerable: false, writable: true }, // default : Absolute Expiration

    expirationHour: { value: 1, enumerable: false, writable: true }    // default expiration : 1 hour

});


window.cache.data.setObject = function (data) {

    let self = this;

    self.set(self.cacheData, data, self.expirationHour); // expiration hour : default 1 hour

}


window.cache.data.getObject = function () {

    let self = this;

    let data = self.get(self.cacheData, self.isSlidingExpiration, self.expirationHour);


    return data;

}


window.cache.data.removeObject = function () {

    let self = this;

    self.remove(self.cacheData);

}



setObject / getObject  / removeObject  3개의 함수를 가지는 window.cache.data 객체를 만들었다.


옵션으로 cacheData , isSlidingExpiration , expirationHour 값을 가지고 있다.

실제 저장할 데이터에 따라 이 3가지 값을 변경하면서 저장하면된다.


마지막으로, 이 객체를 상속하여, 실제 사용하는 객체를 생성할 것이다.



// Sliding Expiration 캐시 예제

window.cache.data.locale_data = Object.create(window.cache.data, {

    cacheData: { value: "locale_data", enumerable: false, writable: false },

    isSlidingExpiration: { value: true, enumerable: false, writable: true }, // sliding expiration

    expirationHour: { value: 1, enumerable: false, writable: true }    // expiration timeout : 1 hour

});


// Absolute Expiration 캐시 예제

window.cache.data.server_config = Object.create(window.cache.data, {

    cacheData: { value: "server_config", enumerable: false, writable: false },

    isSlidingExpiration: { value: false, enumerable: false, writable: true }, // absolute expiration

    expirationHour: { value: 0.5, enumerable: false, writable: true }    // expiration timeout : 30 minute

});



위에서는 window.cache.data.locale_data 와 window.cache.data.server_config 를 생성했지만,

위의 cacheData string 값을 변경하여, 무한하게 생성해서 사용하면 된다.




사용예1)

window.cache.data.locale_data.setObject( master_data.masterData );

window.cache.data.locale_data.getObject();


사용예2)

window.cache.data.server_config.setObject( master_data.server_config_list_array );

window.cache.data.server_config.getObject();






실제로, locale_data 는 getObject() 를 할때마다, expiratioDate 값이 변하는것을 확인할 수 있다.



주의 : 위의 방식은, 만료시간이 되었다고 해서, 실제로 메모리에서 캐시 데이터가 삭제되는것이 아니라, get 함수를 호출할때, 만료시간을 체크하여, 캐시 데이터를 삭제하는 방식이다.

만약 get / set 등을 하지 않는 시간에도, 만료된 캐시를 삭제하고 싶다면, Observer 객체를 만들어서, 주기적으로 만료시간을 체크하여야 할것이다.



Posted by 헝개
개발팁2017. 12. 18. 13:10

C# 에서 파일을 복사하는 방법은

 

간단하게,

 

System.IO.File.Copy 메서드를 이용하는 방법이다.

 

System.IO.File.Copy( source_file_nm, target_file_nm, is_overwrite);

 

단순하면서도, 적절한 Exception 처리만 해준다면, 아주 강력하다.

 

하지만, 한줄의 명령어로, Synchronous 작업이기 때문에, 파일이 복사 되는 동안에는 아무것도 하지 못한다.

 

파일이 복사되는 동안에, 진행률을 표시하기 위해서는, 위의 파일 복사 방법이 아닌, 비동기 작업 또는 쓰레드 작업이 필요하다.

 

파일이 복사 되는 동안에, 진행률도 표시해야 하고, 프로그레스바도 움직여줘야 하기 때문이다.

 

위의 방법은 한번에 파일복사 명령을 주고, 진행이 끝나면, 알려주는 방법이라면,

 

아래의 방법은, 우리가 직접 파일 복사를 주도하게 될것이다.

 

복사할 source_file_nm 의 경로에서 파일을 조금씩(buffer) 읽어서,  target_file_nm 에다가 write 해주는 방법이며,

 

이것은, loop 를 통해서, 파일의 내용을 모두 복사할때까지, 반복으로 작업을 진행하며,

 

진행하는 중간에 진행률과, 프로그레스바를 같이 움직여 줄것이다.

 

        byte[] buf = new byte[1024 * 512];  // 0.5M

 

        FileInfo file = new FileInfo(source_file_nm);

 

        if (!Directory.Exists(target_path))
                Directory.CreateDirectory(target_path);

 

        FileStream strIn = new FileStream(source_file_nm, FileMode.Open);
        FileStream strOut = new FileStream(Path.Combine(target_path, target_file_nm), FileMode.Create); 

 

 

버퍼를 0.5M 로 했으나, 1M로 해도 상관이 없다.

 

source 파일을 읽기용으로 Open 하고, target 파일을 쓰기용으로 Open 했다.

 

                while (strIn.Position < strIn.Length)
                {
                        int len = strIn.Read(buf, 0, buf.Length);
                        strOut.Write(buf, 0, len);
                }

 

                strOut.Flush();

                strIn.Close();
                strOut.Close();

 

source 를 버퍼로 (0.5M씩) 읽어서, target 파일에다 쓰기를 하고, 완료가 되면, 스트림을 모두 닫아준다.

 

이렇게 하면, 버퍼로 읽어서, 파일에 쓰는 작업은 완료가 되었다.

 

이제, 작업 중간에, 진행률과 프로그레스 바를 움직일 것이다.

 

진행률 표시를 위해 Label 을 하나 만들고, ProgressBar 를 하나 만들어 준다.

 

그리고, 아래와 같이 미리 설정을 해둔다.

 

progressBar.Maximum = Int32.MaxValue;


 

        byte[] buf = new byte[1024 * 512];  // 0.5M

        FileInfo file = new FileInfo(source_file_nm);

 

        if (!Directory.Exists(target_path))
                Directory.CreateDirectory(target_path);

 

        FileStream strIn = new FileStream(source_file_nm, FileMode.Open);
        FileStream strOut = new FileStream(Path.Combine(target_path, target_file_nm), FileMode.Create);

 

        try
        {
                while (strIn.Position < strIn.Length)
                {
                        int len = strIn.Read(buf, 0, buf.Length);
                        strOut.Write(buf, 0, len);

 

                        progressBar.Value = (int)(Int32.MaxValue * strIn.Position / strIn.Length);

                        this.lblBytes.Text = string.Format("{0:N0} / {1:N0}", strIn.Position, strIn.Length);
                }
        }
        catch (Exception ex)
        {
                MessageBox.Show("Error Occured : " + ex.Message, "Error");
                this.DialogResult = System.Windows.Forms.DialogResult.Abort;
        }
        finally
        {
                strOut.Flush();

                strIn.Close();
                strOut.Close();
        }

 

 

이렇게 버퍼로 복사하는 중간에 진행률과 프로그레스바를 표시하도록 수정했다.

그러나, 실제 파일이 복사되는 과정에서, 복사과 완료될동안, 응답없음 상태로 나왔다가, 파일 복사가 끝나야 결과가 나올 것이다.

 

위 작업을 쓰레드로 처리하거나, 백그라운드워커로 실행해줘야 한다.

 

BackgroundWorker 객체를 하나 만들고, 아래와 같이 이벤트 처리를 해준다.

 

this.bg_filecopy = new BackgroundWorker();
this.bg_filecopy.WorkerSupportsCancellation = true;
this.bg_filecopy.DoWork += new DoWorkEventHandler(bg_filecopy_DoWork);
this.bg_filecopy.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_filecopy_RunWorkerCompleted);

위에서 만들어준 코드는 bg_filecopy_DoWork 메서드 안에 넣어주면 된다.

 

이로써, 파일이 복사되는 중간에 상태표시가 되는것을 확인할 수 있다.

하지만, 뭔가 이상한 점이 있다.

 

위에서 만들어준 byte 버퍼를 사용하였지만, 실제 시스템에서는 또다른 버퍼가 있다는 것이다.

 

위에서 마지막 Flush 는 버퍼에 있는것을 모두 파일로 쓸 것이다.

하지만, Loop 안에 strOut.Write(buf, 0, len); 가 있지만, 시스템은 계속 버퍼에 쌓아두었다가 일정 시점 이후에 쓰기를 하는 것을 볼 수 있다.

 

따라서, Loop 안에서도 일정 시점마다  Flush 를 하도록 수정하는것이 좋다.

 

 

 

 

 

Posted by 헝개
개발팁2017. 11. 1. 14:39

C# WinForm 의 GroupBox 컨트롤의 title 영역에는 text 가 표시가 되지만,

 

이부분에 Checkbox 를 넣고, 체크박스가 선택된 경우에 GroupBox 가 활성화 되도록 하는 방법이다.

 

Form 안에 GroupBox 와 CheckBox 컨트를을 각각 1개씩 생성한다.

 

GroupBox 의 Text 값은 비워둔다.

CheckBox 의 Text 값은 적당한 값으로 적어준다.

 

CheckBox 의 위치는 아무곳에나 두어도 상관이 없다.

 

CheckBox Control 에서 마우스 오른쪽 버튼을 눌러서,

 

맨앞으로 가져오기 를 한번 클릭해준다.

 

그래야, GroupBox 에 CheckBox 가 감춰지지 않고 잘 보여지게 된다.

 

그리고, FORM 의 생성자를 아래와 같이 정의하자

 

public CustomForm()
{

    InitializeComponent();


    // 이부분을 추가한다.
    this.chkImageChange.Location = new Point(this.group_image.Location.X + 13, this.group_image.Location.Y - 1);
    this.chkVideoChange.Location = new Point(this.group_video.Location.X + 13, this.group_video.Location.Y - 1);

 

이부분은, CheckBox 컨트롤의 위치를 GroupBox컨트를의 title 영역위에 위치하도록 해주는 부분다.

 

이제 CheckBox 컨트롤의 이벤트를 추가할 것이다.

 

CheckedChanged 이벤트를 아래와 같이 정의한다.

 

private void chkImageChange_CheckedChanged(object sender, EventArgs e)
{
    this.group_image.Enabled = this.chkImageChange.Checked;

 

CheckBox 컨트롤을 체크하면, 그룹박스가 활성화되고, 해제되면, 그룹박스도 비활성화 될것이다.

 

이제, Form 에서 기본값으로 활성화 할지, 비활성화 할지를 선택하면 된다.

 


실행 화면 : 아래와 같은 형태로 나올 것이다.


 

 

 

Posted by 헝개
개발팁2017. 10. 28. 17:14

C1FlexGrid 를 새로 만들면, 기본적으로 1번째 column 이 회색으로 나온다.

이 부분을 fixed 라고 부르는데, 이 부분에 행번호(Line Number)를 표시하는 방법이다.

 

 

 

Caption : No 라고 적고,

DataType : Int32 로 변경한다.

Name : 은 비워둔다.

 

물론 Data Source 에 행번호를 넣어서 바인딩 할 수도 있지만, 이렇게 되면, Sort, Filtering 하게 되면, 행번호가 꼬이게 된다.

 

데이터로써가 아니라 항상 위에서 1부터 시작하는 행번호를 넣을 것이다.

 

 

 

 

그리드의 속성창에서 DrawMode 를 Normal -> OwnerDraw 로 변경한다.

 

 

 

이제, 그리드의 OwnerDrawCell 이벤트를 아래와 같이 정의한다.

 

private void FlexGrid_OwnerDrawCell(object sender, OwnerDrawCellEventArgs e)
{
 if ((e.Row >= this.Rows.Fixed) && (e.Col == (this.Cols.Fixed - 1)))
 {
     e.Text = ((e.Row - this.Rows.Fixed) + 1).ToString();
 }
}

 

이제, 빌드 해서 실행해보면 행번호가 잘 나올것이다.

 

 

[Sample]

 

 

Posted by 헝개
개발팁2017. 5. 16. 14:29

맥어드레스는 NIC 컨트롤러의 물리적인 주소로, 일반적으로는 변경이 안되는것으로 알려져 있다.

네트웍상에서, 원격PC를 구분하는 목적으로 사용되기도 하는 값으로, 16진수의 값으로 구성되어 있다.

 

본인 PC의 맥어드레스를 알고 싶다면, cmd 창를 띄우고, ipconfig -all 이라고 치면된다.

 

1개 이상의 이더넷 어댑터가 나오는데, 이는 유선랜,무선랜,VM,VPN 등 PC에 설치된 가상의 장치까지 모두 포함되기 때문이다.

 

본인의 NIC 이름에 맞는 이더넷 어댑터로 확인하면 된다.

 

 

물리적 주소 : A0-B0-C0-D0-E0-F0 형식으로 된 6자리 값이 맥어드레스 이다.

 

이 물리적 주소 값을 바꾸기 위해서는, 컴퓨터관리 -> 장치관리자로 이동한다.

 

 

장치관리자에서, 네트워크 어댑터를 선택하고, 본인의 NIC 컨트롤러의 속성창을 띄운다.

 

고급탭의 속성창에 보면, 네트워크 주소가 있다. 이를 선택하면,

 

기본값은 없음 으로 되어 있다.

 

 

 

이곳에, 본인이 변경하고 싶은 16진수 값을 입력한다. 이때 대쉬(-) 문자는 입력하지 않는다.

 

 

입력을 마치고, cmd 창에서, ipconfig -all 을 쳐보면, 맥어드레스가 변경된 것을 확인할 수 있다.

 

참고로, 가정용으로 ISP 로 인터넷을 이용할때, 보통 유동IP 인데, 거의 고정된 아이피를 사용하게 된다.

이때, 위의 방법으로, 맥어드레스를 변경하면, 본인의 IP 주소도 변경될 것이다.

IP주소를 바꾸고 싶을때, 이 방법을 써도 된다.

 

Posted by 헝개
개발팁2016. 12. 5. 11:25

안드로이드 스튜디오 2.1 에서 정상적으로 나오던것이

안드로이드 스튜디오 2.2 로 업데이트 되면서, IDE 화면에서 한글이 깨지는 현상이 있다.

 

AVD에서는 정상적으로 한글이 나오는데, IDE 툴 안에서만 한글이 깨지는 현상으로,

한글 FONT 설정이 잘못되어서 그렇다.

 

 

 

TEXT 에디터로 아래 경로로 이동하여,

C:\Program Files\Android\Android Studio\plugins\android\lib\layoutlib\data\fonts

fonts.xml 파일을 열어보자.

 

"ko" 로 검색해보면, 한글 폰트가, 엉뚱한 NotoSansCJK-Regular.ttc 로 되어 있는것이 보인다.

 

 

 

이를, NanumGothic.ttf 으로 변경하고, 저장한다.

안드로이드 스튜디오를 재시작해보면, 한글이 정상적으로 나올것이다.

 

 

Posted by 헝개
개발팁2016. 10. 12. 09:54

MSSQL 서버 버전 확인방법으로, SQL Server Managerment Studio 에서

속성을 통해서 확인할 수 도 있지만, 쿼리문을 통해서 확인하는 방법이다.

 

1. @@version 을 통한 방법

 

SELECT @@version

 

출력예)

Microsoft SQL Server 2012 - 11.0.2218.0 (X64)
 Jun 12 2012 13:05:25
 Copyright (c) Microsoft Corporation
 Enterprise Edition (64-bit) on Windows NT 6.2 <X64> (Build 9200: )

 

2. SERVERPROPERTY 를 이용한 방법

 

SELECT SERVERPROPERTY('productversion'), SERVERPROPERTY ('productlevel'), SERVERPROPERTY ('edition')

출력예)

10.50.1600.1    RTM    Standard Edition (64-bit)

 

productversion 은 제품 버전을 뜻하고,

productlevel 은 제품 수준을 뜻하고,

edition 은 에디션을 뜻한다.

 

productversion 에 나오는 값은 아래와 같이 정의할 수 있다.

 

 12.0.2000.80

 SQL Server 2014 RTM

 11.00.3000.00

 SQL Server 2012 서비스 팩 1

 11.00.2100.60

 SQL Server 2012 RTM

 10.50.4000.0

 SQL Server 2008 R2 서비스 팩 2

 10.50.2500.0

 SQL Server 2008 R2 서비스 팩 1

 10.50.1600.1

 SQL Server 2008 R2 RTM

 10.00.5500.00

 SQL Server 2008 서비스 팩 3

 10.00.4000.00

 SQL Server 2008 서비스 팩 2

 10.00.2531.00

 SQL Server 2008 서비스 팩 1

 10.00.1600.22

 SQL Server 2008 RTM

 9.00.5000.00

 SQL Server 2005 서비스 팩 4

 9.00.4035

 SQL Server 2005 서비스 팩 3

 9.00.3042

 SQL Server 2005 서비스 팩 2

 9.00.2047

 SQL Server 2005 서비스 팩 1

 9.00.1399

 SQL Server 2005 RTM

 8.00.2039

 SQL Server 2000 서비스 팩 4

 8.00.760

 SQL Server 2000 서비스 팩 3

 8.00.534

 SQL Server 2000 서비스 팩 2

 8.00.384

 SQL Server 2000 서비스 팩 1

 8.00.194

 SQL Server 2000 RTM

 

Posted by 헝개
개발팁2016. 9. 23. 15:46

Ellipsis 라는것은 말줄임표시를 뜻한다.

 

웹에서 그리드를 구현하다보면, 정해진 컬럼 사이즈보다 컨텐츠의 텍스트 길이가 긴 경우,

줄바꿈이 되어 2줄로 나오거나, 컬럼사이즈가 늘어나거나 한다.

 

이때, 컬럼사이즈는 고정을 시키고, 줄바꿈이나, 컬럼이 늘어나지 않게 하는 방법이

CSS 의 text-overflow 의 ellipsis 속성 값이다.

 

스타일 속성을 아래와 같이 정의한다.

 

<style>

.ShowEllipsisTooltip {
    overflow:hidden;
    text-overflow:ellipsis;
    white-space:nowrap;
}

</style>

 

이제 그리드 컨텐츠 부분의 class 속성을 위에서 만든 스타일을 적용하면 된다.

 

<td class="ShowEllipsisTooltip">컨텐츠 내용 어쩌고 저쩌고</td>

 

이제 웹브라우저 화면상에서는 컬럼사이즈보다 컨텐츠 길이가 긴경우 뒷부분이 잘리고, … 문자가 표시될 것이다.

 

여기서, Ellipsis 가 적용된 컨텐츠에 마우스를 올려놓으면, 전체 컨텐츠가 Tooltip (툴팁) 으로 표시되도록 해보자.

 

 <script>

        $('.ShowEllipsisTooltip').each(function () {
            if (this.offsetWidth < this.scrollWidth)
                $(this).attr('title', $(this).text());
        });
</script>

 

class 가 ShowEllipsisTooltip 인 객체(dom)를 찾아서, 말줄임 된 만, title 속성을 주도록 했다.

즉, 말줄임표시가 없는것은 무시하고, 말줄임 된 것만 찾아서, 툴팁을 표시하도록 한다.

 

 

 

실제 사이트에 적용한 화면이며, 말줌일 표시가 있는 컨텐츠 위에 마우스를 올렸을때만, 위와같이 전제 텍스트가 툴팁으로 표시된다.

 

 

 

Posted by 헝개
개발팁2016. 9. 12. 13:48

jquery 를 이용한 드래그앤드랍 파일업로드 방법이다.

 

우선, 파일을 탐색기에서 드래그하여, 드랍할 영역을 아래와 같이 만든다.

 

<style>
    #dropzone
    {
        border:2px dotted #3292A2;
        width:90%;
        height:50px;
        color:#92AAB0;
        text-align:center;
        font-size:24px;
        padding-top:12px;
        margin-top:10px;
    }
</style>

 

<div id="dropzone">Drag & Drop Files Here</div> 

 

웹브라우저로 보면, 아래와 같이 나타나게 된다.

 

 

이제 div 영역에 아래와 같이, 자바스트립트로 drag & drop 을 허용해준다.

 

$(function () {
     var obj = $("#dropzone");

     obj.on('dragenter', function (e) {
          e.stopPropagation();
          e.preventDefault();
          $(this).css('border', '2px solid #5272A0');
     });

     obj.on('dragleave', function (e) {
          e.stopPropagation();
          e.preventDefault();
          $(this).css('border', '2px dotted #8296C2');
     });

     obj.on('dragover', function (e) {
          e.stopPropagation();
          e.preventDefault();
     });

     obj.on('drop', function (e) {
          e.preventDefault();
          $(this).css('border', '2px dotted #8296C2');

          var files = e.originalEvent.dataTransfer.files;
          if(files.length < 1)
               return;

          F_FileMultiUpload(files, obj);
     });

});

 

div 영역의 dotted 테두리에 드래그 하여 마우스를 올리면, 아래와 같이 solid 테두리로 변경된다.

 

 

drop 이벤트를 보면, e.originalEvent.dataTransfer.files 에서 멀티업로드시, drop 했던 파일 목록을 가져와서, 업로드 하는 함수를 호출하고 있다. 이 영역이 파일뿐만 아니라, 다른 것들도 drag and drop 이 허용되기 때문에, 파일이 있는 경우만, 업로드 처리를 하고 있다.

 

이제 실제, 서버로 업로드 하는 자바스크립트는 아래와 같다.

 

// 파일 멀티 업로드
function F_FileMultiUpload(files, obj) {
     if(confirm(files.length + "개의 파일을 업로드 하시겠습니까?") ) {
         var data = new FormData();
         for (var i = 0; i < files.length; i++) {
            data.append('file', files[i]);
         }

         var url = "<서버 파일업로드 URL>";
         $.ajax({
            url: url,
            method: 'post',
            data: data,
            dataType: 'json',
            processData: false,
            contentType: false,
            success: function(res) {
                F_FileMultiUpload_Callback(res.files);
            }
         });
     }
}

// 파일 멀티 업로드 Callback
function F_FileMultiUpload_Callback(files) {
     for(var i=0; i < files.length; i++)
         console.log(files[i].file_nm + " - " + files[i].file_size);
}

 

 

drop 했던 파일 목록은, FormData 형태의 file 배열로 넘긴다.

이는 INPUT 태그의 file 과 같다.

서버의 <서버 파일업로드 URL> 은 php, asp.net, java 등의 기존 업로드 로직을 그대로 사용하면 된다.

서버쪽은, 다르게 처리할 부분이 전혀없다.

 

예제에서 사용한 서버코드는 asp.net 에서, Request.Files 로 업로드 파일을 저장하고, 저장된 결과의 파일명과 파일사이즈를 json 형태로 리턴하였다.

 

 

Posted by 헝개
개발팁2016. 9. 1. 10:29

엑셀 다운로드 구현시 html 의 table 태그로 구성된 파일로 내려받기를 하는경우,

 

ContentType 을 application/vnd.ms-excel
하고, 출력을 하면, 자동으로 엑셀로 열리게 되는데,

 

이때 엑셀에서는, 본문에 있는 셀 데이터 (table 태그) 값에 따라,
자료형을 자동으로 인식하게 된다.

 

000212 이런값이 있을경우, 엑셀에서는 숫자로 인식하여,
앞에 0을 떼어버리고, 212 로 출력을 하게 된다.

 

이러한 현상을 해결하기 위해서는 해당 컬럼이 문자라는것을 인식시켜줄 필요가 있다.

 

<style>
body {
    font-size:12px;
    font-family: 맑은 고딕;
}

table {
    font-size:12px;
    text-align:center;
}
table th {
    text-align:center;
    height:28px;
    background-color: #f0f0f0;
}
table td {
    text-align:left;
    height:26px;
    mso-number-format:"\@";
}

.number {
    text-align:right !important;
    mso-number-format:General !important;
    white-space:nowrap !important;
}
</style>

 

html 파일의 head 태그 사이에, 위와 같이 css 를 정의해보자.
mso-number-format 은 MS Office 에서 숫자 포맷관련 stylesheet 속성이다.

 

 <table border="1">
    <tr>
        <th>발주일</th>
        <th>C/D 번호</th>
        <th>순번</th>
        <th>부품번호</th>
        <th>부품명</th>
        <th>수량</th>
    </tr>
    <tr>
        <td>2016-08-31</td>
        <td>00212</td>
        <td class="number">1</td>
        <td>HGTG5026</td>
        <td>INLAY CARRIER SET FOR MACH2XS 15G</td>
        <td class="number">11</td>
    </tr>
</table>

 

이제 본문을 table th td 로 구성하면, 자동으로 문자로 인식하도록 한다.
단, 실제 숫자로 인식해야 할 곳에는, class="number" 만 지정해주면 된다.

 

 

 

Posted by 헝개