반응형



안드로이드 갤러리에 있는 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);  
   }
       
}  





반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts