반응형

Sicos1977/MSGReader: C# Outlook MSG file reader without the need for Outlook (github.com)

 

Msgreader는 이메일 파일인 .msg와 .eml확장자 파일을 읽을 수 있도록하는 라이브러리입니다.

원본 github주소는 위 그림을 클릭하면 이동하실 수 있습니다.

 

이메일 파일에 담긴 이메일 정보(보낸이, 받는이, 보낸시각, 받은시각, cc, bcc, 메일제목, 메일내용, 첨부파일 등)의 정보를 읽을 수 있도록합니다.

 

상세한 예외처리는 제외하고 용법을 보겠습니다.

 

먼저, 메일정보를 쉽게 주고받을 수 있도록 프로퍼티를 충분히 가진 클래스를 선언하겠습니다.

 

class MailFile {
    public string FileDirectory { get; set; }          // 파일 경로
    public string FileName { get; set; }               // 파일명
    public string FilePath { get; set; }               // 파일경로 + 파일명
    public string Originator { get; set; }             // 보낸사람
    public string Addressee { get; set; }              // 받는사람
    public string CC { get; set; }                     // CC
    public string BCC { get; set; }                    // BCC
    public DateTime SentDate { get; set; }             //  발신 시간
    public DateTime ReceivedDate { get; set; }         // 수신 시간
    
    public MailFile(string fileDirectory, string fileName)
    {
        FileDirectory = fileDirectory;
        FileName = fileName;
        FilePath = Path.Combine(fileDirectory, fileName);  
        // 그냥 string으로 더하지 않고 Path.Combine으로 쓴이유를 모른다면 독자는 꼭 찾아봐야한다!
    }
}

 

 

예제이기 때문에 msg인지 eml파일인지 여부를 비교할 대상은 그냥 선언하겠습니다.

(여기서는 확장자로만 구분합니다.)

// 메일 파일을 읽기전에 있어야할 것들을 기입한 부분입니다.
// msg인지 eml인지 비교할 확장자,
// MailFile 클래스의 선언
// 생성된 첨부파일을 다운받을 경로 등

string msgExtention = ".MSG";
string emlExtention = ".EML";

string mailFileDirectory = @"파일경로, \가 보통들어가서 @를 붙임";
string mailFileName = @"파일명";

var mailFile = new MailFile(mailFileDirectory, mailFileName);
string downloadDirectory = @"첨부파일이있다면 다운받을 경로이다.";

 

다음은 메일파일의 정보가 있는지 여부를 받아서 받아오는 것이다.

FileInfo mailFileInfo = new FileInfo(mailFile.FilePath);

if (fileInfo.Exists && fileInfo.Length > 0)	
// fileInfo.Exists는 파일이 존재하는지 여부를 리턴
// fileInfo.Length는 파일의 크기를 byte단위로 리턴
{
    if (mailFileInfo.Extension.ToUpper().Equals(msgExtention))
    // 파일 경로 비교 ToUpper()를 해도되고 StringComparison.OrdinalIgnoreCase 를 사용해도된다.
    {
        using var msgFile = new MsgReader.Outlook.Storage.Message(mailFilePath);
        // 메일 파일의 경로에 따라 여기서는 확장자값이 .MSG이면 msg인스턴스(Storage.Message)를 생성한다.
        
        // 아래는 읽으면 감이 올 것이다. 각 정보를 받아오는 것이고 
        // 여기에 매칭되도록 초반에 MailFile을 선언했다.
        // 실제 사용시에는 MailFile에 첨부파일 존재여부도 넣어주면 좋다. 
        mailFile.Originator = msg.Sender?.DisplayName;                                    // 보낸이
        mailFile.Addressee = msg.GetEmailRecipients(RecipientType.To, false, false);      // 받는이
        mailFile.CC = msg.GetEmailRecipients(RecipientType.Cc, false, false);             // CC
        mailFile.BCC = msg.GetEmailRecipients(RecipientType.Bcc, false, false);           // BCC
        mailFile.SentDate = msg.SentOn ?? DateTime.MinValue;                              // 보낸시각
        mailFile.ReceivedDate = msg.ReceivedOn ?? DateTime.MinValue;                      // 받는시각
    	
        // 첨부파일은 아래와 같은방법으로 가져올 수 있다.  
        // #### 첨부파일류들은 data[]로 파일데이터를 들고있기때문에 아주 무겁다. ####
        // ## 그러나 IDisposable을 구현하고 있어서 dispose()나 using을 사용할 수 있다.
        // ## 해당부분은 여기에서는 포함하고 있지 않아서 참고하여 개발시 이 부분을 반드시 확인하기바란다.
        // ## 아니면 메모리가 탈탈 털리는 현상을 볼 지도...
        // ## 또한 파일데이터를 들고있기때문에 너무 용량이 큰 친구들은 이것으로 첨부파일 뽑으려면
        // ## 메모리가 아주커야할 것이다... 파일 용량에 꼭 제한을 두자.
        // ## 도입부에 FileInfo 선언했는데 거기서 처리할 수 있다.
        var attachmentsObject = msgFile.Attachments;

        attachmentsObject.ForEach(attachment =>
        {
            try
            {
                if (!Directory.Exists(downloadDirectory)){    // 첨부파일을 다운받을 경로가 없으면 만든다.
                    Directory.CreateDirectory(downloadDirectory);
                }
                
                if (attachment != null && attachment is Storage.Attachment)  // 일반적인 첨부파일이면
                {
                    var attachmentCast = attachment as Storage.Attachment ?? throw new Exception($"형변환했는데 null이면 던질 예외");
                    string attachmentPath = Path.Combine(downloadDirectory, attachmentCast.FileName);             // 내려받을 첨부파일 path
                    File.WriteAllBytes(attachmentPath, attachmentCast.Data);                                      // 다운로드           
                }
                else if (attachment != null && attachment is Storage.Message)  // 앞에 using var msgFile = new MsgReader.Outlook.Storage.Message(mailFilePath); 부분을 보면 눈치 챘을 수도 있지만 이는 첨부파일이 MSG파일인 경우이다.
                {
                  var attachmentCast = attachment as Storage.Message ?? throw new Exception($"형변환했는데 null이면 던질 예외");
                  string attachmentPath = Path.Combine(downloadDirectory, attachmentCast.FileName);
                  attachmentCast.Save(attachmentPath);                       // Storage.Message는 Save함수를 사용하여 파일을 내려받아야 한다.
                }
                else
                {
                  // 둘다 아닌경우에 취할 조치를 하면 된다. 예외처리를 해도 좋다.
                }
            }
            catch (Exception ex)
            {
              Log.Error(ex.ToString());
            }
      });
    }
    else if (mailFileInfo.Extension.ToUpper().Equals(emlExtention))
    {
        var emlFile = MsgReader.Mime.Message.Load(mailFileInfo);    // eml 파일을 읽는 방법이다.

        if (emlFile.Headers != null)    // eml 파일의 메일 파일정보 불러오기는 복잡하다... msgReader 공식 문서를 참조하면 이렇게 해야한다.
        {
            if (emlFile.Headers.From != null && !string.IsNullOrEmpty(eml.Headers.From.DisplayName))
            { mailFile.Originator = emlFile.Headers.From.DisplayName.ToString().Replace("\"", string.Empty); }
            else if (eml.Headers.From != null && !string.IsNullOrEmpty(eml.Headers.From.Address))
            { mailFile.Originator = emlFile.Headers.From.Address.ToString().Replace("\"", string.Empty); }
            else { mailFile.Originator = ""; }
        
            emlFile.Headers.To.ForEach(to => mailInfo.Addressee += "," + to.ToString());
            if (mailFile.Addressee == null) { mailFile.Addressee = ""; }
            else { mailFile.Addressee = mailFile.Addressee.Substring(1).Replace("\"", string.Empty); }
        
            emlFile.Headers.Cc.ForEach(cc => mailFile.CC += "," + cc.ToString());
            if (mailFile.CC == null) { mailFile.CC = ""; }
            else { mailFile.CC = mailFile.CC.Substring(1).Replace("\"", string.Empty); }
        
            emlFile.Headers.Bcc.ForEach(bcc => mailFile.BCC += "," + bcc.ToString());
            if (mailFile.BCC == null) { mailFile.BCC = ""; }
            else { mailFile.BCC = mailInfo.BCC.Substring(1).Replace("\"", string.Empty); }
        
            if (emlFile.Headers.DateSent != null) { mailFile.SentDate = emlFile.Headers.DateSent; }
            else { mailFile.SentDate = DateTime.MinValue; }
        
            if (emlFile.Headers.Received != null && emlFile.Headers.Received.Count > 0)
            {
                mailFile.ReceivedDate = emlFile.Headers.Received.Last().Date;
            }
            else
            {
                mailFile.ReceivedDate = DateTime.MinValue;
            }
        }    // 여기까지가 메일정보 읽기
        
        ObservableCollection<MessagePart> attachments = emlFile.Attachments;    
        var attachmentsCount = attachments.Count;       
    
        foreach (var attachment in attachments) // eml파일은 정보읽기는 복잡하지만 첨부파일 다운로드는 간단하다.
        { 
            try
            {
                if (!Directory.Exists(downloadDirectory)) 
                    Directory.CreateDirectory(downloadDirectory);
    
                if (attachment.IsAttachment)
                    attachment.Save(new FileInfo(Path.Combine(downloadDirectory, attachment.FileName)));             
                
            }
            catch (Exception ex)
            {
                Log.Error(ex.ToString());
            }
        }
    }
}

 

생각보다 코드가 길기 때문에 설명도 코드블럭내에 같이 적어놨습니다. 특히 ####, ## 붙은 부분을 반드시 읽어서 참고시에 메모리 등 관리문제를 최대한 방지 하시기 바랍니다.

 

반응형

+ Recent posts