博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dax圣经 翻新_使用翻新和Node JS的Android图像上传
阅读量:2532 次
发布时间:2019-05-11

本文共 16864 字,大约阅读时间需要 56 分钟。

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 jdservercd jdservernpm install expressnpm 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 = "" +"
" +"
" +"
" +"";app.get('/', function (req, res){ res.writeHead(200, {'Content-Type': 'text/html' }); res.end(form);});// Include the node file modulevar 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 filesapp.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 in the terminal.

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

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 is needed since Android Nougat for fetching image path.

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

(Code)

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

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

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
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
permissionsToRequest; private ArrayList
permissionsRejected = new ArrayList<>(); private ArrayList
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
allIntents = new ArrayList<>(); PackageManager packageManager = getPackageManager(); Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); List
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
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
findUnAskedPermissions(ArrayList
wanted) { ArrayList
result = new ArrayList
(); 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
req = apiService.postImage(body, name); req.enqueue(new Callback
() { @Override public void onResponse(Call
call, Response
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
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文件,您可以使用该文件来设置服务器,如开头所述。

翻译自:

dax圣经 翻新

转载地址:http://juqzd.baihongyu.com/

你可能感兴趣的文章
漫漫修行路
查看>>
js与jQuery的区别——每日一记录
查看>>
MyBatis 处理sql中的 大于,小于,大于等于,小于等于
查看>>
Lunix文件的读写权限问题
查看>>
Liferay 7:portlet name
查看>>
PostgreSQL9.6.3的REDIS测试
查看>>
解决pycharm问题:module 'pip' has no attribute 'main'
查看>>
002 lambda表达式
查看>>
springboot添加自定义注解
查看>>
POJ 2391 Ombrophobic Bovines ( 经典最大流 && Floyd && 二分 && 拆点建图)
查看>>
JavaScript数组方法之reduce
查看>>
Linux常用命令之文件搜索命令
查看>>
thinkphp自定义权限管理之名称判断
查看>>
C++ ORM ODB 入门介绍(一)
查看>>
C#_02.14_基础五_.NET类
查看>>
Flask 学习资源
查看>>
Android SDK下载和更新失败的解决方法 分类: Android...
查看>>
MVC2 强类型的 HTML Helper
查看>>
开发 Windows 8 应用 - 0 - windows 8 开发资源
查看>>
生成二维码图片的工具类
查看>>