dax圣经 翻新_使用翻新和Node JS的Android图像上传

dax圣经 翻新

In this tutorial, we’ll be creating an android application that uploads an image using Retrofit MultiPart Request to the localhost server. We’ll create a simple server using Node JS first. Let’s get started.

在本教程中,我们将创建一个Android应用程序,该应用程序使用Retrofit MultiPart Request将图像上传到本地服务器。 我们将首先使用Node JS创建一个简单的服务器。 让我们开始吧。

设置节点JS服务器 (Setting Up Node JS Server)

Let’s set up a simple Node JS localhost server where we can upload files.

让我们设置一个简单的Node JS localhost服务器,在其中可以上传文件。

We’ll start by creating a separate directory for the node js server. Let’s name it jdserver
Inside the directory, we’ll install the following packages using npm. Ensure that node and npm are installed.

我们将从为节点js服务器创建一个单独的目录开始。 让我们将其命名为jdserver
在目录内,我们将使用npm安装以下软件包。 确保已安装nodenpm

mkdir jdserver
cd jdserver
npm install express
npm install multer

Multer: is an image upload library. It handles getting formdata from requests.
Express is a popular web framework.

Multer:是图像上传库。 它处理从请求获取formdata
Express是一种流行的Web框架。

Let’s create a subfolder uploads which will contain the uploaded images by doing mkdir uploads.

让我们创建一个子文件夹uploads ,通过执行mkdir uploads来包含上载的图像。

Inside the jdserver directory, we’ll create a multipart.js file which contains the code for the server setup and uploading the image:

jdserver目录中,我们将创建一个multipart.js文件,其中包含服务器设置和上传图像的代码:

var express = require("express");
var app = express();
var multer, storage, path, crypto;
multer = require('multer')
path = require('path');
crypto = require('crypto');

var form = "<!DOCTYPE HTML><html><body>" +
"<form method='post' action='/upload' enctype='multipart/form-data'>" +
"<input type='file' name='upload'/>" +
"<input type='submit' /></form>" +
"</body></html>";

app.get('/', function (req, res){
  res.writeHead(200, {'Content-Type': 'text/html' });
  res.end(form);

});

// Include the node file module
var fs = require('fs');

storage = multer.diskStorage({
    destination: './uploads/',
    filename: function(req, file, cb) {
      return crypto.pseudoRandomBytes(16, function(err, raw) {
        if (err) {
          return cb(err);
        }
        return cb(null, "" + (raw.toString('hex')) + (path.extname(file.originalname)));
      });
    }
  });


// Post files
app.post(
  "/upload",
  multer({
    storage: storage
  }).single('upload'), function(req, res) {
    console.log(req.file);
    console.log(req.body);
    res.redirect("/uploads/" + req.file.filename);
    console.log(req.file.filename);
    return res.status(200).end();
  });

app.get('/uploads/:upload', function (req, res){
  file = req.params.upload;
  console.log(req.params.upload);
  var img = fs.readFileSync(__dirname + "/uploads/" + file);
  res.writeHead(200, {'Content-Type': 'image/png' });
  res.end(img, 'binary');

});


app.listen(3000);

The upload file name is changed using the crypto package and stored in the uploads directory.
The server would run on 3000 port on the localhost.

使用加密软件包更改上传文件名,并将其存储在上载目录中。
服务器将在本地主机上的3000端口上运行。

In order to start the server, do the following:
node multipart.js from the jdserver directory on the terminal.

为了启动服务器,请执行以下操作:
终端上jdserver目录中的node multipart.js

When you open your localhost in the web browser (127.0.0.1:3000), this is what you should see:

当您在网络浏览器(127.0.0.1:3000)中打开本地主机时,应该看到以下内容:

Thus we are able to upload an image to the server. It gets saved in the uploads dir as shown below:

因此,我们能够将图像上传到服务器。 它被保存在上uploads目录中,如下所示:

In order to test whether the POST works, we can install the httpie in the terminal.

为了测试POST是否有效,我们可以在终端中安装httpie

One way is by using homebrew.

一种方法是使用自制程序。

brew install httpie

To test the POST for multipart image upload we can run the following on the terminal:

要测试用于多部分图像上传的POST,我们可以在终端上运行以下命令:

http -f POST 127.0.0.1:3000/upload name='upload' upload@wallpaper.png

wallpaper.png if exists in the current directory in the terminal would be uploaded.

如果存在于终端的当前目录中,则将上载wallpaper.png文件。

This gets logged in the terminal:

这将登录到终端:

Now that all is set on the server side, let’s implement the same in our Android Application.

现在,所有操作都在服务器端进行了设置,让我们在我们的Android应用程序中实现相同的功能。

改造多部分上传 (Retrofit MultiPart Upload)

Sending an Image via a network call is different from sending text/plain or x-www-urlencoded requests which are essentially texts/key value pairs respectively.

通过网络呼叫发送图像不同于发送文本/纯文本或x-www-urlencoded请求,它们本质上分别是文本/键值对。

In order to send an Image, we need to create a MultiPartRequest.
In Retrofit, we need to use MultipartBody.Part and RequestBody for uploading the image.
MultipartBody.Part is used to pass the file and RequestBody is used to pass the plain text here.

为了发送图像,我们需要创建一个MultiPartRequest
在Retrofit中,我们需要使用MultipartBody.Part和RequestBody来上传图像。
MultipartBody.Part用于传递文件,而RequestBody用于传递纯文本。

In order to use MultiPart we need to annotate the POST request in retrofit with @Multipart

为了使用MultiPart我们需要在@Multipart改造中注释POST请求。

In the following section, we’ll use MultiPart for Image Uploading to the Node JS server hosted locally.

在以下部分中,我们将使用MultiPart将图像上传到本地托管的Node JS服务器。

项目结构 (Project Structure)

The build.gradle includes the following dependencies:

build.gradle包括以下依赖项:

implementation 'com.android.support:design:28.0.0'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'

The AndroidManifest.xml looks like this:

AndroidManifest.xml如下所示:

We’ve added the relevant permissions and FileProvider is needed since Android Nougat for fetching image path.

自Android Nougat以来,我们已经添加了相关权限,并且需要FileProvider来获取图像路径。

(Code)

The code for the activity_main.xml layout is given below:

下面给出了activity_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">


    <RelativeLayout
        android:id="@+id/content_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">


        <TextView
            android:id="@+id/textView"
            android:layout_centerHorizontal="true"
            android:textAppearance="@style/TextAppearance.AppCompat.Display1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />


        <ImageView
            android:id="@+id/imageView"
            android:layout_width="250dp"
            android:layout_height="250dp"
            android:layout_centerInParent="true"
            android:adjustViewBounds="true"
            android:scaleType="centerCrop" />

    </RelativeLayout>


    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="16dp"
        app:srcCompat="@android:drawable/ic_menu_camera" />


    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fabUpload"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        android:layout_margin="16dp"
        app:srcCompat="@android:drawable/ic_menu_upload" />


</android.support.design.widget.CoordinatorLayout>

ApiService.java

ApiService.java

package com.journaldev.androiduploadimageretrofitnodejs;

import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;

interface ApiService {
    @Multipart
    @POST("/upload")
    Call<ResponseBody> postImage(@Part MultipartBody.Part image, @Part("upload") RequestBody name);
}

Inside @Part annotation name we must specify the key which is the same as the one defined in the js file earlier i.e. “upload” along with the File.

@Part批注名称中,我们必须指定与js文件中先前定义的键相同的键,即“ upload”和File。

MainActivity.java

MainActivity.java

package com.journaldev.androiduploadimageretrofitnodejs;

import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;

import static android.Manifest.permission.CAMERA;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    ApiService apiService;
    Uri picUri;
    private ArrayList<String> permissionsToRequest;
    private ArrayList<String> permissionsRejected = new ArrayList<>();
    private ArrayList<String> permissions = new ArrayList<>();
    private final static int ALL_PERMISSIONS_RESULT = 107;
    private final static int IMAGE_RESULT = 200;
    FloatingActionButton fabCamera, fabUpload;
    Bitmap mBitmap;
    TextView textView;


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

        fabCamera = findViewById(R.id.fab);
        fabUpload = findViewById(R.id.fabUpload);
        textView = findViewById(R.id.textView);
        fabCamera.setOnClickListener(this);
        fabUpload.setOnClickListener(this);
        
        askPermissions();
        initRetrofitClient();


    }

    private void askPermissions() {
        permissions.add(CAMERA);
        permissions.add(WRITE_EXTERNAL_STORAGE);
        permissions.add(READ_EXTERNAL_STORAGE);
        permissionsToRequest = findUnAskedPermissions(permissions);


        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


            if (permissionsToRequest.size() > 0)
                requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT);
        }
    }

    private void initRetrofitClient() {
        OkHttpClient client = new OkHttpClient.Builder().build();

        apiService = new Retrofit.Builder().baseUrl("https://192.168.88.65:3000").client(client).build().create(ApiService.class);
    }


    public Intent getPickImageChooserIntent() {

        Uri outputFileUri = getCaptureImageOutputUri();

        List<Intent> allIntents = new ArrayList<>();
        PackageManager packageManager = getPackageManager();

        Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
        List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
        for (ResolveInfo res : listCam) {
            Intent intent = new Intent(captureIntent);
            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
            intent.setPackage(res.activityInfo.packageName);
            if (outputFileUri != null) {
                intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
            }
            allIntents.add(intent);
        }

        Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
        galleryIntent.setType("image/*");
        List<ResolveInfo> listGallery = packageManager.queryIntentActivities(galleryIntent, 0);
        for (ResolveInfo res : listGallery) {
            Intent intent = new Intent(galleryIntent);
            intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
            intent.setPackage(res.activityInfo.packageName);
            allIntents.add(intent);
        }

        Intent mainIntent = allIntents.get(allIntents.size() - 1);
        for (Intent intent : allIntents) {
            if (intent.getComponent().getClassName().equals("com.android.documentsui.DocumentsActivity")) {
                mainIntent = intent;
                break;
            }
        }
        allIntents.remove(mainIntent);

        Intent chooserIntent = Intent.createChooser(mainIntent, "Select source");
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, allIntents.toArray(new Parcelable[allIntents.size()]));

        return chooserIntent;
    }


    private Uri getCaptureImageOutputUri() {
        Uri outputFileUri = null;
        File getImage = getExternalFilesDir("");
        if (getImage != null) {
            outputFileUri = Uri.fromFile(new File(getImage.getPath(), "profile.png"));
        }
        return outputFileUri;
    }

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


        if (resultCode == Activity.RESULT_OK) {

            ImageView imageView = findViewById(R.id.imageView);

            if (requestCode == IMAGE_RESULT) {


                String filePath = getImageFilePath(data);
                if (filePath != null) {
                    mBitmap = BitmapFactory.decodeFile(filePath);
                    imageView.setImageBitmap(mBitmap);
                }
            }

        }

    }


    private String getImageFromFilePath(Intent data) {
        boolean isCamera = data == null || data.getData() == null;

        if (isCamera) return getCaptureImageOutputUri().getPath();
        else return getPathFromURI(data.getData());

    }

    public String getImageFilePath(Intent data) {
        return getImageFromFilePath(data);
    }

    private String getPathFromURI(Uri contentUri) {
        String[] proj = {MediaStore.Audio.Media.DATA};
        Cursor cursor = getContentResolver().query(contentUri, proj, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA);
        cursor.moveToFirst();
        return cursor.getString(column_index);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putParcelable("pic_uri", picUri);
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        picUri = savedInstanceState.getParcelable("pic_uri");
    }

    private ArrayList<String> findUnAskedPermissions(ArrayList<String> wanted) {
        ArrayList<String> result = new ArrayList<String>();

        for (String perm : wanted) {
            if (!hasPermission(perm)) {
                result.add(perm);
            }
        }

        return result;
    }

    private boolean hasPermission(String permission) {
        if (canMakeSmores()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED);
            }
        }
        return true;
    }

    private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) {
        new AlertDialog.Builder(this)
                .setMessage(message)
                .setPositiveButton("OK", okListener)
                .setNegativeButton("Cancel", null)
                .create()
                .show();
    }

    private boolean canMakeSmores() {
        return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1);
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

        switch (requestCode) {

            case ALL_PERMISSIONS_RESULT:
                for (String perms : permissionsToRequest) {
                    if (!hasPermission(perms)) {
                        permissionsRejected.add(perms);
                    }
                }

                if (permissionsRejected.size() > 0) {


                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                        if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) {
                            showMessageOKCancel("These permissions are mandatory for the application. Please allow access.",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            requestPermissions(permissionsRejected.toArray(new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT);
                                        }
                                    });
                            return;
                        }
                    }

                }

                break;
        }

    }

    private void multipartImageUpload() {
        try {
            File filesDir = getApplicationContext().getFilesDir();
            File file = new File(filesDir, "image" + ".png");

            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            mBitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
            byte[] bitmapdata = bos.toByteArray();


            FileOutputStream fos = new FileOutputStream(file);
            fos.write(bitmapdata);
            fos.flush();
            fos.close();


            RequestBody reqFile = RequestBody.create(MediaType.parse("image/*"), file);
            MultipartBody.Part body = MultipartBody.Part.createFormData("upload", file.getName(), reqFile);
            RequestBody name = RequestBody.create(MediaType.parse("text/plain"), "upload");

            Call<ResponseBody> req = apiService.postImage(body, name);
            req.enqueue(new Callback<ResponseBody>() {
                @Override
                public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {

                    if (response.code() == 200) {
                        textView.setText("Uploaded Successfully!");
                        textView.setTextColor(Color.BLUE);
                    }

                    Toast.makeText(getApplicationContext(), response.code() + " ", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onFailure(Call<ResponseBody> call, Throwable t) {
                    textView.setText("Uploaded Failed!");
                    textView.setTextColor(Color.RED);
                    Toast.makeText(getApplicationContext(), "Request failed", Toast.LENGTH_SHORT).show();
                    t.printStackTrace();
                }
            });


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.fab:
                startActivityForResult(getPickImageChooserIntent(), IMAGE_RESULT);
                break;

            case R.id.fabUpload:
                if (mBitmap != null)
                    multipartImageUpload();
                else {
                    Toast.makeText(getApplicationContext(), "Bitmap is null. Try again", Toast.LENGTH_SHORT).show();
                }
                break;
        }
    }
}

In the above code, we need to ask for runtime permissions before anything.
The image bitmap retrieved from the camera/gallery image is eventually passed to the server in the multipart Retrofit request.

在上面的代码中,我们需要先获得运行时权限。
从摄像机/图库图像检索到的图像位图最终在多部分改造请求中传递到服务器。

The base URL is the same as the IP of your system if you’re running the application on your phone connected via USB.

如果您正在通过USB连接的手机上运行应用程序,则基本URL与系统的IP地址相同。

In the above code, the base URL is my current IP. You need to change it to yours when running the project.

在上面的代码中,基本URL是我当前的IP。 运行项目时,需要将其更改为您自己的。

Make sure that the server is up and running!

确保服务器已启动并正在运行!

The output of the above application in action is given below:

上面应用程序的输出如下:

And the file is now visible in the uploads directory:

现在,该文件在uploads目录中可见:

That brings an end to this tutorial. You can download the project from the link below. It contains the multipart.js file which you can use to set up the server as explained at the beginning.

这样就结束了本教程。 您可以从下面的链接下载项目。 它包含multipart.js文件,您可以使用该文件来设置服务器,如开头所述。

翻译自: https://www.journaldev.com/23709/android-image-uploading-with-retrofit-and-node-js

dax圣经 翻新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值