ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android 갤러리에 있는 이미지 파일을 PC JAVA 서버로 전송하기
    Android/이미지 전송 2018. 9. 13. 13:42



    안드로이드 갤러리에 있는 JPG 이미지 파일을 PC의 자바 서버 프로그램으로 전송하는 예제입니다.

    최근들어 관련 질문이 많아져서 만들어봤습니다. 참고하세요.



    기존 포스팅의 코드를 수정해서 사용해야 합니다. 우선 다음 포스팅을 해본 후 이번 포스팅을 이어서 진행하세요.



    Android와 PC JAVA 프로그램 간 블루투스 통신 예제

    http://webnautes.tistory.com/849




    최초 작성 - 2018. 9. 13


    실행 결과

    1. 자바 서버 프로그램을  먼저 실행해둡니다.


    [Thu Sep 13 13:01:05 KST 2018] Local Bluetooth device...

    BlueCove version 2.1.1-SNAPSHOT on winsock
    [Thu Sep 13 13:01:06 KST 2018] address: F8633F2710E0
    [Thu Sep 13 13:01:06 KST 2018] name: WEBNAUTES-PC
    [Thu Sep 13 13:01:06 KST 2018] Opened connection successful.
    [Thu Sep 13 13:01:06 KST 2018] Server is now running.
    [Thu Sep 13 13:01:06 KST 2018] wait for client requests...




    2. 안드로이드 앱에서 연결합니다.





    3. IMAGE 버튼을 선택합니다.




    4. 갤러리에서 전송할 이미지를 선택합니다.





    5. 전송이 완료되었습니다.





    6. 서버에서도 전송결과를 확인할 수 있습니다.


    [Thu Sep 13 13:02:07 KST 2018] Me : 에코 서버에 접속하셨습니다.
    [Thu Sep 13 13:02:07 KST 2018] Me : 보내신 문자를 에코해드립니다.
    [Thu Sep 13 13:02:07 KST 2018] ready
    [Thu Sep 13 13:02:21 KST 2018] D013FDEE432E: [Start28874]
    [Thu Sep 13 13:02:21 KST 2018] start recv image 28874
    read 1011/28874  bytes.
    read 2022/28874  bytes.
    read 3033/28874  bytes.
    read 4096/28874  bytes.
    read 5107/28874  bytes.
    read 6118/28874  bytes.
    read 7129/28874  bytes.
    read 8140/28874  bytes.
    read 9151/28874  bytes.
    read 10162/28874  bytes.
    read 11173/28874  bytes.
    read 12184/28874  bytes.
    read 13195/28874  bytes.
    read 14206/28874  bytes.
    read 15217/28874  bytes.
    read 16228/28874  bytes.
    read 17239/28874  bytes.
    read 18250/28874  bytes.
    read 19261/28874  bytes.
    read 20272/28874  bytes.
    read 21283/28874  bytes.
    read 22294/28874  bytes.
    read 23305/28874  bytes.
    read 24316/28874  bytes.
    read 25327/28874  bytes.
    read 26338/28874  bytes.
    read 27349/28874  bytes.
    read 28360/28874  bytes.
    read 28874/28874  bytes.
    [Thu Sep 13 13:02:21 KST 2018] end
    [Thu Sep 13 13:02:21 KST 2018] Me : recv Image
    [Thu Sep 13 13:02:21 KST 2018] ready




    7. 서버 프로그램 폴더에서 전송받은 이미지를 확인할 수 있습니다.





    안드로이드 코드 수정

    갤러리에서 이미지 파일을 가져오는 코드와 런타임 퍼미션 코드를  추가합니다.


    1. 메니페스트 파일에  다음 권한을 추가합니다.


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




    2. build.gradle에 com.android.support:design를 추가합니다.  퍼미션 관련 코드에서 Snackbar를 사용하기 위해 필요합니다.


    dependencies {
       implementation fileTree(dir: 'libs', include: ['*.jar'])
       implementation 'com.android.support:appcompat-v7:27.1.1'
       implementation 'com.android.support.constraint:constraint-layout:1.1.2'
       testImplementation 'junit:junit:4.12'
       androidTestImplementation 'com.android.support.test:runner:1.0.2'
       androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
       implementation 'com.android.support:design:27.1.1'
    }




    3. 레이아웃 파일 activity_main.xml에 이미지 전송을 위한 버튼을 추가합니다. Snackbar를 사용하기 위해 레이아웃에 id도 추가해야 합니다.


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
       xmlns:android="http://schemas.android.com/apk/res/android"
       android:orientation="vertical"
       android:paddingBottom="10dp"
       android:paddingTop="10dp"
       android:id="@+id/layout_main"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

       <LinearLayout
           android:weightSum="1"
           android:orientation="horizontal"
           android:layout_width="match_parent"
           android:layout_height="wrap_content">

           <EditText
               android:id="@+id/input_string_edittext"
               android:hint="input text here"
               android:layout_weight="0.6"
               android:layout_width="0dp"
               android:layout_height="wrap_content"/>

           <Button
               android:id="@+id/send_button"
               android:layout_weight="0.2"
               android:layout_width="0dp"
               android:layout_height="wrap_content"
               android:text="Send" />

           <Button
               android:id="@+id/send_image_button"
               android:layout_weight="0.2"
               android:layout_width="0dp"
               android:layout_height="wrap_content"
               android:text="Image" />
       </LinearLayout>

      . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .





    4. MainActivity.java 파일에 갤러리로부터 파일을 가져와 전송하는 코드를 추가합니다.


    public class MainActivity extends AppCompatActivity
           implements ActivityCompat.OnRequestPermissionsResultCallback {

       private final int REQUEST_BLUETOOTH_ENABLE = 100;
       private final int GET_GALLERY_IMAGE = 200;


       private View mLayout;  // Snackbar 사용하기 위해서는 View가 필요합니다.

       private TextView mConnectionStatus;



       @Override
       public void onCreate(Bundle savedInstanceState)
       {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);

           mLayout = findViewById(R.id.layout_main);




           Button sendButton = (Button)findViewById(R.id.send_button);
           sendButton.setOnClickListener(new View.OnClickListener(){
               public void onClick(View v){
                   String sendMessage = mInputEditText.getText().toString();
                   if ( sendMessage.length() > 0 ) {
                       sendMessage(sendMessage);
                   }
               }
           });

           Button sendImageButton = (Button)findViewById(R.id.send_image_button);
           sendImageButton.setOnClickListener(new View.OnClickListener(){
               public void onClick(View v){
                   Intent intent = new Intent(Intent.ACTION_PICK);
                   intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                   intent.setType("image/*");
                   startActivityForResult(intent, GET_GALLERY_IMAGE);

               }
           });

           
           mConnectionStatus = (TextView)findViewById(R.id.connection_status_textview);



       @Override
       protected void onActivityResult(int requestCode, int resultCode, Intent data) {

           if(requestCode == REQUEST_BLUETOOTH_ENABLE){
               if (resultCode == RESULT_OK){
                   //BlueTooth is now Enabled
                   showPairedDevicesListDialog();
               }
               if(resultCode == RESULT_CANCELED){
                   showQuitDialog( "You need to enable bluetooth");
               }
           }else if ( requestCode == GET_GALLERY_IMAGE){
               sendPicture(data.getData());

           }
       }



       private void sendPicture(Uri imgUri) {

           String imagePath = getRealPathFromURI(imgUri);

           Log.e(TAG, imagePath);

           File file = new File(imagePath);
           sendMessage("Start"+file.length());


           try
           {
               FileInputStream fis = new FileInputStream(imagePath);

               byte[] buffer = new byte[4096];

               int totalSize = fis.available();
               int readSize = 0;
               while ( (readSize=fis.read(buffer)) > 0) {

                   mConnectedTask.mOutputStream.write(buffer, 0, readSize);
               }

               fis.close();
    ;
           }
           catch (Exception e)
           {
               e.printStackTrace();
           }

           mConversationArrayAdapter.insert("Me:  " + "Send Image" , 0);


       }


       private String getRealPathFromURI(Uri contentUri) {

           String[] proj = {MediaStore.Images.Media.DATA};
           Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
           cursor.moveToFirst();
           int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);

           return cursor.getString(column_index);
       }





    5.  안드로이드 6.0 이상의 경우 이미지에 접근하기 위해 런타임 퍼미션도 필요합니다. 코드 설명은 다음 포스팅을 참고하세요.



    안드로이드 런타임 퍼미션(Runtime Permission) 예제

    http://webnautes.tistory.com/1225




          Button sendImageButton = (Button)findViewById(R.id.send_image_button);
           sendImageButton.setOnClickListener(new View.OnClickListener(){
               public void onClick(View v){
                   Intent intent = new Intent(Intent.ACTION_PICK);
                   intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                   intent.setType("image/*");
                   startActivityForResult(intent, GET_GALLERY_IMAGE);

               }
           });


           int writeExternalStoragePermission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
           if ( writeExternalStoragePermission != PackageManager.PERMISSION_GRANTED){
               
               if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])) {

                  
                   Snackbar.make(mLayout, "이 앱을 실행하려면 카메라와 외부 저장소 접근 권한이 필요합니다.",
                           Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                       @Override
                       public void onClick(View view) {

                           ActivityCompat.requestPermissions( MainActivity.this, REQUIRED_PERMISSIONS,
                                   PERMISSIONS_REQUEST_CODE);
                       }
                   }).show();


               } else {

                   ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS,
                           PERMISSIONS_REQUEST_CODE);
               }

           }


           mConnectionStatus = (TextView)findViewById(R.id.connection_status_textview);



               else {

               Log.d(TAG, "Initialisation successful.");


               showPairedDevicesListDialog();

           }

       }



       private static final int PERMISSIONS_REQUEST_CODE = 100;
       String[] REQUIRED_PERMISSIONS  = {Manifest.permission.WRITE_EXTERNAL_STORAGE};

       @Override
       public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grandResults) {

           if ( requestCode == PERMISSIONS_REQUEST_CODE && grandResults.length == REQUIRED_PERMISSIONS.length) {
      

               boolean check_result = true;



               for (int result : grandResults) {
                   if (result != PackageManager.PERMISSION_GRANTED) {
                       check_result = false;
                       break;
                   }
               }



               if ( check_result ) {
                   ;
               }
               else {
             
                   if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])) {



                       Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 앱을 다시 실행하여 퍼미션을 허용해주세요. ",
                               Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                           @Override
                           public void onClick(View view) {

                               finish();
                           }
                       }).show();

                   }else {


                       Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 설정(앱 정보)에서 퍼미션을 허용해야 합니다. ",
                               Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                           @Override
                           public void onClick(View view) {

                               finish();
                           }
                       }).show();
                   }
               }

           }


       }


       @Override
       protected void onDestroy() {




    자바 전체 코드

    import java.io.BufferedReader;
    import java.io.BufferedWriter;
    import java.io.DataInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    import java.io.Reader;
    import java.nio.charset.Charset;
    import java.nio.charset.StandardCharsets;
    import java.util.Date;

    import javax.bluetooth.BluetoothStateException;
    import javax.bluetooth.LocalDevice;
    import javax.bluetooth.RemoteDevice;
    import javax.bluetooth.UUID;
    import javax.microedition.io.Connector;
    import javax.microedition.io.StreamConnection;
    import javax.microedition.io.StreamConnectionNotifier;


    public class Server{
     
       public static void main(String[] args){
       
       
    log("Local Bluetooth device...\n");
           
        LocalDevice local = null;
    try {

    local = LocalDevice.getLocalDevice();
    } catch (BluetoothStateException e2) {

    }   

        log( "address: " + local.getBluetoothAddress() );
        log( "name: " + local.getFriendlyName() );
       
       
        Runnable r = new ServerRunable();
        Thread thread = new Thread(r);
        thread.start();
       
       }
       
         
       private static void log(String msg) {  
       
           System.out.println("["+(new Date()) + "] " + msg);  
       }

    }


    class ServerRunable implements Runnable{
     
    //UUID for SPP
    final UUID uuid = new UUID("0000110100001000800000805F9B34FB", false);
       final String CONNECTION_URL_FOR_SPP = "btspp://localhost:"
        + uuid +";name=SPP Server";
     
       private StreamConnectionNotifier mStreamConnectionNotifier = null;  
       private StreamConnection mStreamConnection = null;
       private int count = 0;
       private boolean recvImage = false;
      
       
    @Override
    public void run() {

        try {
       
    mStreamConnectionNotifier = (StreamConnectionNotifier) Connector
    .open(CONNECTION_URL_FOR_SPP);

    log("Opened connection successful.");
    } catch (IOException e) {

    log("Could not open connection: " + e.getMessage());
    return;
    }
      

        log("Server is now running.");

       
       
           while(true){
           
            log("wait for client requests...");

    try {

    mStreamConnection = mStreamConnectionNotifier.acceptAndOpen();
    } catch (IOException e1) {

    log("Could not open connection: " + e1.getMessage() );
    }

           
    count++;
    log("현재 접속 중인 클라이언트 수: " + count);


           new Receiver(mStreamConnection).start();
           }

    }

           
       
       class Receiver extends Thread {
       
        private InputStream mInputStream = null;
           private OutputStream mOutputStream = null;
           private String mRemoteDeviceString = null;
           private StreamConnection mStreamConnection = null;
           
           
           Receiver(StreamConnection streamConnection){
           
            mStreamConnection = streamConnection;

    try {
       
    mInputStream = mStreamConnection.openInputStream();
    mOutputStream = mStreamConnection.openOutputStream();

    log("Open streams...");
    } catch (IOException e) {

    log("Couldn't open Stream: " + e.getMessage());

    Thread.currentThread().interrupt();
    return;
    }


    try {
           
    RemoteDevice remoteDevice
    = RemoteDevice.getRemoteDevice(mStreamConnection);

           mRemoteDeviceString = remoteDevice.getBluetoothAddress();
           
    log("Remote device");
    log("address: "+ mRemoteDeviceString);
           
    } catch (IOException e1) {

    log("Found device, but couldn't connect to it: " + e1.getMessage());
    return;
    }

    log("Client is connected...");
           }
           
           
         
           
           
        @Override
    public void run() {
       
    try {

        Reader mReader = new BufferedReader(new InputStreamReader
            ( mInputStream, Charset.forName(StandardCharsets.UTF_8.name())));

        boolean isDisconnected = false;
        StringBuilder imageStringBuilder = null;
       
    Sender("에코 서버에 접속하셨습니다.");
    Sender( "보내신 문자를 에코해드립니다.");
       
    while(true){

    log("ready");

           
               StringBuilder stringBuilder = new StringBuilder();
               int c = 0;
               
               
    while ( '\n' != (char)( c = mReader.read()) ) {

    if ( c == -1){

    log("Client has been disconnected");

    count--;
    log("현재 접속 중인 클라이언트 수: " + count);

    isDisconnected = true;
    Thread.currentThread().interrupt();

    break;
    }

    stringBuilder.append((char) c);
    }

               if ( isDisconnected ) break;
               
               String recvMessage = stringBuilder.toString();
           log( mRemoteDeviceString + ": [" + recvMessage +"]" );
           
           if (recvMessage.matches("Start.*")) {
            recvImage = true;

            log("start recv image "+ recvMessage.substring(5));
            int size = Integer.parseInt(recvMessage.substring(5));
           
        DataInputStream dis = new DataInputStream(mInputStream);
        FileOutputStream fos = new FileOutputStream("testfile.jpg");
        byte[] buffer = new byte[4096];
       
        int read = 0;
        int totalRead = 0;

        while((read = dis.read(buffer)) > 0) {
        totalRead += read;

        System.out.println("read " + totalRead + "/" + size + "  bytes.");
       
        fos.write(buffer, 0, read);
       
        if ( totalRead >= size) break;
        }
       
        log("end");
       
        fos.flush();
        fos.close();
       
        recvImage = false;
        recvMessage = "recv Image";
           }
           


           if (recvImage == false )
            Sender(recvMessage);

    }

    } catch (IOException e) {

    log("Receiver closed" + e.getMessage());
    }
    }
       

        void Sender(String msg){
           
               PrintWriter printWriter = new PrintWriter(new BufferedWriter
                (new OutputStreamWriter(mOutputStream,
                Charset.forName(StandardCharsets.UTF_8.name()))));
           
        printWriter.write(msg+"\n");
        printWriter.flush();
       
        log( "Me : " + msg );
        }
    }
       
       
       private static void log(String msg) {  
       
           System.out.println("["+(new Date()) + "] " + msg);  
       }
           
    }  





    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    댓글 19

    • ma 2018.09.17 23:03


      좋은 정보 감사합니다. 어플을 다 만들어서 보내는데 겔러리에서 사진 선택까지는 되는데 선택 후 파일 보내지는 처리가 아무런 변화가 없습니다... 조언좀 해주세요

    • nam 2018.09.18 12:14


      어플 예제를 따라 하여 잘되어서 좋습니다 ㅎㅎ 근데
      Intent intent = new Intent(Intent.ACTION_PICK);
      intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
      intent.setType("image/*");
      startActivityForResult(intent, GET_GALLERY_IMAGE);

      이부분이 버튼이 눌리면 이 함수가 겔러리를 호출하는걸 알겠습니다
      겔러리에서 호출말고 이부분을 경로에 있는 걸 호출 하고 싶은데 조언 부탁드립니다.

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2018.09.18 12:21 신고


        버튼 누르면 파일 경로를 인자로 sendPicture함수를 호출하도록 하세요.

        sendPicture 함수 수정이 필요할수도 있습니다.

    • 발빠른감자 2019.07.21 01:25


      안녕하세요 글 정말 잘봤습니다!

      혹시 궁금한 점이 있는데 안드로이드에서 블루투스 없이 동영상을 파이썬 서버로 보내려면 어떻게 해야할지 정말 궁금합니다.

      파이썬 서버에서 딥러닝알고리즘을 돌리려고 합니다!

      블로그 즐겨찾기하고 잘보겠습니다!

      감사합니다!

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.07.21 09:35 신고


        간단하게..

        소켓을 사용하여 이미지 한장씩 안드로이드에서 파이썬 서버로 전송하면 될듯합니다.

        연속적으로 이미지를 보내고 받는 쪽에서 연속적으로 수신된 이미지를 보여주면 동영상처럼 보입니다.



      • 발빠른동전 2019.07.21 17:04


        답변 정말 감사드립니다.

        바로 시도해보겠습니다!

    • 아부츠 2019.09.22 20:00


      안녕하세요 설명을 친절하게 해주셔서 고민이 많았던 부분을 해결했습니다!
      감사합니다 ㅎㅎ

      그런데 안드로이드에서 서버가 아닌 반대방향
      즉, 서버에서 안드로이드로 보내는 것도 가능한건가요?

    • wha_D 2019.12.13 16:44


      이미지를 선택하면 어플이 종료되고 서버측에서도 온갖문자만 나오고 전송이 완료되지 않는데 혹시 무슨문제인지 알 수 있을까요? 안스를 처음써봐서 많이 어렵네요 ㅠㅠ

    • edw 2020.02.13 10:12


      혹시 스마트폰 - pc 말구 스마트폰 - 스마트폰 간의 이미지 전송도 될까요?
      단순 메시지 전송은 되는데 이미지를 전송하려나 감이 잘 안잡히네요..

Designed by Tistory.