본문 바로가기
Dev Talks/Mobile App Dev

[Android] 웹서버를 통해 apk 파일 배포하기

by 곰씨네IT 2016. 6. 24.

보통 안드로이드 app 개발 후 apk 파일을 배포하기 위해서는 구글 플레이 스토어를 사용하지요. 

하지만 기업 모바일의 경우 (B2E) 사내 사용자를 위한 app 을 자체 시스템 또는 웹페이지를 통해서 

배포하는 경우가 많습니다. 


그러면 웹페이지를 통한 apk 파일 배포는 어떻게 하는지 살펴보겠습니다.

우선 웹서버가 있어야겠죠. 웹서버는 apache, nginx 를 해도 되고 윈도우 서버인 경우 IIS 를 사용하면 됩니다.

(참고글 : 웹서버의 종류 및 설치 방법)


그러면 웹서버 설치가 끝나셨으면 이제 apk 파일을 배포해 봅시다.


<a태그> 를 이용하여 다운로드하는 방법

이 방법은 간단합니다. 웹서버에 다운로드 페이지 하나 만들어서 

<a href="appname.apk" type="application/vnd.android.package-archive">App 다운로드</a>

를 달아주시면 됩니다. 이 방법의 단점은 사용자가 위 링크를 터치했을 때 다운로드가 진행되지만 자동 설치까지는 

되지 않습니다. 즉, 사용자가 다운로드 완료되었다는 알림을 선택하고 설치화면으로 이동하여 설치를 하여야 합니다. 

여기서 사용자 폰 설정에서는 "알 수 없는 소스 설치" 가 체크되어 있어야 하죠.


그리고 주의해야할 점은 웹서버 설정 중에 apk 를 application/vnd.android.package-archive 로 설정했는지 체크해봐야 

합니다. 위에 이미 type을 지정하긴 했지만 빼먹을 수 있으니 체크하는게 좋겠죠? 


리눅스에서는 /etc/mime.types 파일을 편집한후 application/vnd.android.package-archive apk 를 추가하고 웹서버 

재기동을 하면 됩니다.


(참고) 안드로이드 네이티브 소스로 웹서버에 있는 파일을 다운로드 하는 방법

앱 자체에 다운로드 버튼을 두고 사용자가 그 버튼을 눌렀을 때 웹서버에 있는 파일을 다운로드 하는 방법입니다.

안드로이드 프로그래밍 정복'(한빛미디어) (Page. 780) - 김상형님 저서의 예제소스 또는 다음 소스를 참고하시면 됩니다.


MainActivity.java

  1. public class MainActivity extends Activity implements OnClickListener {  
  2.     /** Called when the activity is first created. */  
  3.     String File_Name = "확장자를 포함한 파일명";   
  4.     String File_extend = "확장자명";   
  5.   
  6.     String fileURL = "웹서버 쪽 파일이 있는 경로"// URL  
  7.     String Save_Path;   
  8.     String Save_folder = "/mydown";  
  9.   
  10.     ProgressBar loadingBar;  
  11.     DownloadThread dThread;  
  12.   
  13.     @Override  
  14.     public void onCreate(Bundle savedInstanceState) {  
  15.         super.onCreate(savedInstanceState);  
  16.         setContentView(R.layout.main);  
  17.   
  18.         Button btn = (Button) findViewById(R.id.downbtn);  
  19.         btn.setOnClickListener(this);  
  20.   
  21.         loadingBar = (ProgressBar) findViewById(R.id.Loading);  
  22.   
  23.         // 다운로드 경로를 외장메모리 사용자 지정 폴더로 함.  
  24.         String ext = Environment.getExternalStorageState();  
  25.         if (ext.equals(Environment.MEDIA_MOUNTED)) {  
  26.             Save_Path = Environment.getExternalStorageDirectory()  
  27.                     .getAbsolutePath() + Save_folder;  
  28.         }  
  29.     }  
  30.   
  31.     @Override  
  32.     public void onClick(View view) {  
  33.         // TODO Auto-generated method stub  
  34.         if (view.getId() == R.id.downbtn) {  
  35.             File dir = new File(Save_Path);  
  36.             // 폴더가 존재하지 않을 경우 폴더를 만듦  
  37.             if (!dir.exists()) {  
  38.                 dir.mkdir();  
  39.             }  
  40.   
  41.             // 다운로드 폴더에 동일한 파일명이 존재하는지 확인해서  
  42.             // 없으면 다운받고 있으면 해당 파일 실행시킴.  
  43.             if (new File(Save_Path + "/" + File_Name).exists() == false) {  
  44.                 loadingBar.setVisibility(View.VISIBLE);  
  45.                 dThread = new DownloadThread(fileURL + "/" + File_Name,  
  46.                         Save_Path + "/" + File_Name);  
  47.                 dThread.start();  
  48.             } else {  
  49.                 showDownloadFile();  
  50.             }  
  51.         }  
  52.     }  
  53.   
  54.     // 다운로드 쓰레드로 돌림..  
  55.     class DownloadThread extends Thread {  
  56.         String ServerUrl;  
  57.         String LocalPath;  
  58.   
  59.         DownloadThread(String serverPath, String localPath) {  
  60.             ServerUrl = serverPath;  
  61.             LocalPath = localPath;  
  62.         }  
  63.   
  64.         @Override  
  65.         public void run() {  
  66.             URL imgurl;  
  67.             int Read;  
  68.             try {  
  69.                 imgurl = new URL(ServerUrl);  
  70.                 HttpURLConnection conn = (HttpURLConnection) imgurl  
  71.                         .openConnection();  
  72.                 int len = conn.getContentLength();  
  73.                 byte[] tmpByte = new byte[len];  
  74.                 InputStream is = conn.getInputStream();  
  75.                 File file = new File(LocalPath);  
  76.                 FileOutputStream fos = new FileOutputStream(file);  
  77.                 for (;;) {  
  78.                     Read = is.read(tmpByte);  
  79.                     if (Read <= 0) {  
  80.                         break;  
  81.                     }  
  82.                     fos.write(tmpByte, 0, Read);  
  83.                 }  
  84.                 is.close();  
  85.                 fos.close();  
  86.                 conn.disconnect();  
  87.   
  88.             } catch (MalformedURLException e) {  
  89.                 Log.e("ERROR1", e.getMessage());  
  90.             } catch (IOException e) {  
  91.                 Log.e("ERROR2", e.getMessage());  
  92.                 e.printStackTrace();  
  93.             }  
  94.             mAfterDown.sendEmptyMessage(0);  
  95.         }  
  96.     }  
  97.   
  98.     Handler mAfterDown = new Handler() {  
  99.         @Override  
  100.         public void handleMessage(Message msg) {  
  101.             // TODO Auto-generated method stub  
  102.             loadingBar.setVisibility(View.GONE);  
  103.             // 파일 다운로드 종료 후 다운받은 파일을 실행시킨다.  
  104.             showDownloadFile();  
  105.         }  
  106.   
  107.     };  
  108.   
  109.     private void showDownloadFile() {  
  110.         Intent intent = new Intent();  
  111.         intent.setAction(android.content.Intent.ACTION_VIEW);  
  112.         File file = new File(Save_Path + "/" + File_Name);  
  113.   
  114.         // 파일 확장자 별로 mime type 지정해 준다.  
  115.         if (File_extend.equals("mp3")) {  
  116.             intent.setDataAndType(Uri.fromFile(file), "audio/*");  
  117.         } else if (File_extend.equals("mp4")) {  
  118.             intent.setDataAndType(Uri.fromFile(file), "vidio/*");  
  119.         } else if (File_extend.equals("jpg") || File_extend.equals("jpeg")  
  120.                 || File_extend.equals("JPG") || File_extend.equals("gif")  
  121.                 || File_extend.equals("png") || File_extend.equals("bmp")) {  
  122.             intent.setDataAndType(Uri.fromFile(file), "image/*");  
  123.         } else if (File_extend.equals("txt")) {  
  124.             intent.setDataAndType(Uri.fromFile(file), "text/*");  
  125.         } else if (File_extend.equals("doc") || File_extend.equals("docx")) {  
  126.             intent.setDataAndType(Uri.fromFile(file), "application/msword");  
  127.         } else if (File_extend.equals("xls") || File_extend.equals("xlsx")) {  
  128.             intent.setDataAndType(Uri.fromFile(file),  
  129.                     "application/vnd.ms-excel");  
  130.         } else if (File_extend.equals("ppt") || File_extend.equals("pptx")) {  
  131.             intent.setDataAndType(Uri.fromFile(file),  
  132.                     "application/vnd.ms-powerpoint");  
  133.         } else if (File_extend.equals("pdf")) {  
  134.             intent.setDataAndType(Uri.fromFile(file), "application/pdf");  
  135.         }  
  136.         startActivity(intent);  
  137.     }  
  138. }  


퍼미션 설정 (androidmanifest.xml)

  1. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
  2. <uses-permission android:name="android.permission.INTERNET"/>  


레이아웃 (layout.xml)

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     android:orientation="vertical"  
  5.     android:layout_width="fill_parent"  
  6.     android:layout_height="fill_parent">  
  7.     <TextView  
  8.         android:layout_width="fill_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="@string/hello" />  
  11.     <ProgressBar  
  12.         xmlns:android="http://schemas.android.com/apk/res/android"  
  13.         android:id="@+id/Loading"  
  14.         android:layout_width="30dip"  
  15.         android:layout_height="30dip"   
  16.         android:visibility="gone"/>  
  17.     <Button  
  18.         android:layout_width="fill_parent"  
  19.         android:layout_height="wrap_content"  
  20.         android:text="다운로드"  
  21.         android:id="@+id/downbtn" />  
  22. </LinearLayout>  

* 출처 : http://motpool.tistory.com/35


다음 참고글 : 앱 업데이트 방법 (1. 구글 플레이스토어 등록 후 업데이트 방법, 2. 별도 내 서버에서 버전관리하는 방법)





댓글