php上传视频至服务器的方法,上传拍下的照片、视频到服务器

我在上一篇教程中给大家讲解了怎么通过进度条下载文件,今天,我将在这篇文章中给大家讲解如果在弹出进度条的同时上传一个文件到服务器。通过阅读这篇文章,并学习其中的知识,你能做出一个类似 Instagram 的App,在你做出的 App 里,你能够像在 Instagram 里那样用摄像头拍照/视频,然后把它们上传到服务器。在服务器端,我使用 PHP 读取上传的文件,并把文件移动到一个特殊的位置。

这篇文章最精华的部分在于:即使是上传大文件,它也能很好地运行,而不是产生许多内存不足的错误。我能有这样的自信,是因为我进行了许多的测试。在测试过程中,即便我上传了一个 50MB 的文件,这个功能仍然运行得很完美,不会出现错误。

准备工作

由于这篇文章需要上传摄像头拍下来的照片/视频,因此你需要先了解有关 Android 摄像头模块的知识。所以我建议你最好先去阅读我之前的一篇有关 Android 是如何操作摄像头的讲解文章,通过阅读这篇文章你能大概了解怎么在你的 Android App 中使用摄像头。

创建 Android 工程

1、 在 Eclipse 中创建一个新的 Android 工程,具体流程如下:New Android Application Project,然后在里面填好一些必要的信息。

2、 打开 res 文件夹中的 strings.xml,然后把下面的内容添加进去:

strings.xml<?xml  version="1.0" encoding="utf-8"?>

Camera File Upload

Capture Image

Record Video

(or)

Upload to Server

3、 向 res 文件夹添加 colors.xml,并把下面的内容添加进去:

colors.xml<?xml  version="1.0" encoding="utf-8"?>

#e8ecfa

#277bec

#ffffff

#4e5572

#1f2649

4、 在 src 目录下创建一个叫做 Config 的类。这个类用于保存文件上传所送到的 URL 地址,还有在移动设备中存储图片/视频的目录名称。你可能需要在测试 App 时使用你自己的 URL 地址进行上传操作。

5、 创建一个叫做 AndroidMultiPartEntity 的类,并把下面的代码复制进去。这个类是一个自定义的 MultipartEntity 类,主要用于提供这个工程中需要用到的关键功能,例如进度条的进度增量。

AndroidMultiPartEntity.javapackage info.androidhive.camerafileupload;

import java.io.FilterOutputStream;    import java.io.IOException;    import java.io.OutputStream;    import java.nio.charset.Charset;

import org.apache.http.entity.mime.HttpMultipartMode;    import org.apache.http.entity.mime.MultipartEntity;

@SuppressWarnings("deprecation")    public class AndroidMultiPartEntity extends MultipartEntity

{

private final ProgressListener listener;

public AndroidMultiPartEntity(final ProgressListener listener) {            super();            this.listener = listener;

}

public AndroidMultiPartEntity(final HttpMultipartMode mode,            final ProgressListener listener) {            super(mode);            this.listener = listener;

}

public AndroidMultiPartEntity(HttpMultipartMode mode, final String boundary,            final Charset charset, final ProgressListener listener) {            super(mode, boundary, charset);            this.listener = listener;

}

@Override

public void writeTo(final OutputStream outstream) throws IOException {            super.writeTo(new CountingOutputStream(outstream, this.listener));

}

public static interface ProgressListener {            void transferred(long num);

}

public static class CountingOutputStream extends FilterOutputStream {

private final ProgressListener listener;            private long transferred;

public CountingOutputStream(final OutputStream out,                final ProgressListener listener) {                    super(out);                    this.listener = listener;                    this.transferred = 0;

}

public void write(byte[] b, int off, int len) throws IOException {

out.write(b, off, len);            this.transferred += len;            this.listener.transferred(this.transferred);

}

public void write(int b) throws IOException {

out.write(b);            this.transferred++;            this.listener.transferred(this.transferred);

}

}

}

现在我们需要通过在 App 中添加一个简单的页面来添加摄像头功能,在这里我们将使用两个 Button 来调用我们的摄像头为我们的 App 拍照/视频。

6、 在你的 AndroidManifest.xml 中添加相应的权限。你会注意到 UploadActivity 也需要被添加到 AndroidManifest.xml 文件中,我们会在后面提到这个。

INTERNET – 联网权限

WRITE_EXTERNAL_STORAGE – 存储照片/视频到本地的权限

RECORD_AUDIO – 拍摄权限

AndroidManifest.xml<?xml  version="1.0" encoding="utf-8"?>

package="info.androidhive.camerafileupload"

android:versionCode="1"

android:versionName="1.0" >

android:minSdkVersion="11"

android:targetSdkVersion="21" />

android:allowBackup="true"

android:icon="@drawable/ic_launcher"

android:label="@string/app_name"

android:theme="@style/AppTheme" >

android:name="info.androidhive.camerafileupload.MainActivity"

android:label="@string/app_name"

android:screenOrientation="portrait" >

android:name="info.androidhive.camerafileupload.UploadActivity"

android:screenOrientation="portrait" >

7、打开你 MainActivity 的布局文件(activity_main.xml),并添加下面的代码,这样做能让你的布局拥有两个 Button。

activity_main.xml<?xml  version="1.0" encoding="utf-8"?>

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="@color/view_background"

android:baselineAligned="false"

android:orientation="vertical" >

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:gravity="center"

android:orientation="vertical" >

android:id="@+id/btnCapturePicture"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="20dp"

android:background="@color/btn_bg"

android:paddingLeft="20dp"

android:paddingRight="20dp"

android:text="@string/btnTakePicture"

android:textColor="@color/white" />

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_marginBottom="20dp"

android:gravity="center_horizontal"

android:text="@string/or"

android:textColor="@color/txt_font" />

android:id="@+id/btnRecordVideo"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:background="@color/btn_bg"

android:paddingLeft="20dp"

android:paddingRight="20dp"

android:text="@string/btnRecordVideo"

android:textColor="@color/white" />

8、在你的 MainActivity 中添加使用摄像头的相关代码,这些代码和这个系列的教程中使用的代码是一样的。简单来说,这个 Activity 主要会完成下面的工作:这个 App 在安装并打开后,能够通过点击 Button 拍照/视频。

只要照片/视频被拍下来,就会保存在移动设备的 SD 卡上

最后, 通过传递 SD 卡中储存媒体文件的对应路径,UploadActivity 会被打开,然后执行上传的流程。

MainActivity.javapackage info.androidhive.camerafileupload;

import java.io.File;    import java.text.SimpleDateFormat;    import java.util.Date;    import java.util.Locale;

import android.app.Activity;    import android.content.Intent;    import android.content.pm.PackageManager;    import android.graphics.Color;    import android.graphics.drawable.ColorDrawable;    import android.net.Uri;    import android.os.Bundle;    import android.os.Environment;    import android.provider.MediaStore;    import android.util.Log;    import android.view.View;    import android.widget.Button;    import android.widget.Toast;

public class MainActivity extends Activity {

// LogCat tag

private static final String TAG = MainActivity.class.getSimpleName();

// Camera activity request codes

private static final int CAMERA_CAPTURE_IMAGE_REQUEST_CODE = 100;        private static final int CAMERA_CAPTURE_VIDEO_REQUEST_CODE = 200;

public static final int MEDIA_TYPE_IMAGE = 1;        public static final int MEDIA_TYPE_VIDEO = 2;

private Uri fileUri; // file url to store image/video

private Button btnCapturePicture, btnRecordVideo;

@Override

protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// Changing action bar background color

// These two lines are not needed

getActionBar().setBackgroundDrawable(new ColorDrawable(Color.parseColor(getResources().getString(R.color.action_bar))));

btnCapturePicture = (Button) findViewById(R.id.btnCapturePicture);

btnRecordVideo = (Button) findViewById(R.id.btnRecordVideo);

/**

* Capture image button click event

*/

btnCapturePicture.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {                    // capture picture

captureImage();

}

});

/**

* Record video button click event

*/

btnRecordVideo.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {                    // record video

recordVideo();

}

});

// Checking camera availability

if (!isDeviceSupportCamera()) {

Toast.makeText(getApplicationContext(),                        "Sorry! Your device doesn't support camera",

Toast.LENGTH_LONG).show();                // will close the app if the device does't have camera

finish();

}

}

/**

* Checking device has camera hardware or not

* */

private boolean isDeviceSupportCamera() {            if (getApplicationContext().getPackageManager().hasSystemFeature(

PackageManager.FEATURE_CAMERA)) {                // this device has a camera

return true;

} else {                // no camera on this device

return false;

}

}

/**

* Launching camera app to capture image

*/

private void captureImage() {

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);

intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

// start the image capture Intent

startActivityForResult(intent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE);

}

/**

* Launching camera app to record video

*/

private void recordVideo() {

Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);

fileUri = getOutputMediaFileUri(MEDIA_TYPE_VIDEO);

// set video quality

intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);

intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); // set the image file

// name

// start the video capture Intent

startActivityForResult(intent, CAMERA_CAPTURE_VIDEO_REQUEST_CODE);

}

/**

* Here we store the file url as it will be null after returning from camera

* app

*/

@Override

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

// save file url in bundle as it will be null on screen orientation

// changes

outState.putParcelable("file_uri", fileUri);

}

@Override

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

// get the file url

fileUri = savedInstanceState.getParcelable("file_uri");

}

/**

* Receiving activity result method will be called after closing the camera

* */

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {            // if the result is capturing Image

if (requestCode == CAMERA_CAPTURE_IMAGE_REQUEST_CODE) {                if (resultCode == RESULT_OK) {

// successfully captured the image

// launching upload activity

launchUploadActivity(true);

} else if (resultCode == RESULT_CANCELED) {

// user cancelled Image capture

Toast.makeText(getApplicationContext(),                            "User cancelled image capture", Toast.LENGTH_SHORT)

.show();

} else {                    // failed to capture image

Toast.makeText(getApplicationContext(),                            "Sorry! Failed to capture image", Toast.LENGTH_SHORT)

.show();

}

} else if (requestCode == CAMERA_CAPTURE_VIDEO_REQUEST_CODE) {                if (resultCode == RESULT_OK) {

// video successfully recorded

// launching upload activity

launchUploadActivity(false);

} else if (resultCode == RESULT_CANCELED) {

// user cancelled recording

Toast.makeText(getApplicationContext(),                            "User cancelled video recording", Toast.LENGTH_SHORT)

.show();

} else {                    // failed to record video

Toast.makeText(getApplicationContext(),                            "Sorry! Failed to record video", Toast.LENGTH_SHORT)

.show();

}

}

}

private void launchUploadActivity(boolean isImage){

Intent i = new Intent(MainActivity.this, UploadActivity.class);

i.putExtra("filePath", fileUri.getPath());

i.putExtra("isImage", isImage);

startActivity(i);

}

/**

* ------------ Helper Methods ----------------------

* */

/**

* Creating file uri to store image/video

*/

public Uri getOutputMediaFileUri(int type) {            return Uri.fromFile(getOutputMediaFile(type));

}

/**

* returning image / video

*/

private static File getOutputMediaFile(int type) {

// External sdcard location

File mediaStorageDir = new File(

Environment

.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),

Config.IMAGE_DIRECTORY_NAME);

// Create the storage directory if it does not exist

if (!mediaStorageDir.exists()) {                if (!mediaStorageDir.mkdirs()) {

Log.d(TAG, "Oops! Failed create "

+ Config.IMAGE_DIRECTORY_NAME + " directory");                    return null;

}

}

// Create a media file name

String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",

Locale.getDefault()).format(new Date());

File mediaFile;            if (type == MEDIA_TYPE_IMAGE) {

mediaFile = new File(mediaStorageDir.getPath() + File.separator

+ "IMG_" + timeStamp + ".jpg");

} else if (type == MEDIA_TYPE_VIDEO) {

mediaFile = new File(mediaStorageDir.getPath() + File.separator

+ "VID_" + timeStamp + ".mp4");

} else {                return null;

}

return mediaFile;

}

}

如果你现在运行你的 App,你会看到类似下面这样的输出结果。

只要你能够打开摄像头并拍着照片/视频,我们就能继续今天的学习,也就是接下来要讲的——创建实现上传功能的 Activity。

9、在 res 文件夹中创建一个 activity_upload.xml 文件,这个布局提供了 ImageView、VideoView 用以预览拍下来的照片/视频,此外,还有一个 ProgressBar 被用于展现上传进度。

activity_upload.xml<?xml  version="1.0" encoding="utf-8"?>

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:background="@color/view_background"

android:orientation="vertical"

android:padding="10dp" >

android:id="@+id/imgPreview"

android:layout_width="fill_parent"

android:layout_height="200dp"

android:visibility="gone"

android:layout_marginTop="15dp"/>

android:id="@+id/videoPreview"

android:layout_width="fill_parent"

android:layout_height="400dp"

android:visibility="gone"

android:layout_marginTop="15dp"/>

android:id="@+id/txtPercentage"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:layout_marginBottom="15dp"

android:layout_marginTop="15dp"

android:textColor="@color/txt_font"

android:textSize="30dp" />

android:id="@+id/progressBar"

style="?android:attr/progressBarStyleHorizontal"

android:layout_width="fill_parent"

android:layout_height="20dp"

android:layout_marginBottom="35dp"

android:visibility="gone"/>

android:id="@+id/btnUpload"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="center_horizontal"

android:background="@color/btn_bg"

android:paddingLeft="20dp"

android:paddingRight="20dp"

android:text="@string/btnUploadToServer"

android:textColor="@color/white"

android:layout_marginBottom="20dp"/>

10、创建一个叫做 UploadActivity 的类,并把下面的代码复制进去。在这个类里,我们主要完成下面两项工作:接收 MainActivity 传过来的媒体文件路径,并把媒体文件显示在我们的视图中供以预览。

通过 UploadFileToServer()方法我们异步处理了上传文件到服务器和更新进度条进度两项工作。

UploadActivity.javapackage info.androidhive.camerafileupload;

import info.androidhive.camerafileupload.AndroidMultiPartEntity.ProgressListener;

import java.io.File;    import java.io.IOException;

import org.apache.http.HttpEntity;    import org.apache.http.HttpResponse;    import org.apache.http.client.ClientProtocolException;    import org.apache.http.client.HttpClient;    import org.apache.http.client.methods.HttpPost;    import org.apache.http.entity.mime.content.FileBody;    import org.apache.http.entity.mime.content.StringBody;    import org.apache.http.impl.client.DefaultHttpClient;    import org.apache.http.util.EntityUtils;

import android.app.Activity;    import android.app.AlertDialog;    import android.content.DialogInterface;    import android.content.Intent;    import android.graphics.Bitmap;    import android.graphics.BitmapFactory;    import android.graphics.Color;    import android.graphics.drawable.ColorDrawable;    import android.os.AsyncTask;    import android.os.Bundle;    import android.util.Log;    import android.view.View;    import android.widget.Button;    import android.widget.ImageView;    import android.widget.ProgressBar;    import android.widget.TextView;    import android.widget.Toast;    import android.widget.VideoView;

public class UploadActivity extends Activity {        // LogCat tag

private static final String TAG = MainActivity.class.getSimpleName();

private ProgressBar progressBar;        private String filePath = null;        private TextView txtPercentage;        private ImageView imgPreview;        private VideoView vidPreview;        private Button btnUpload;        long totalSize = 0;

@Override

protected void onCreate(Bundle savedInstanceState) {            super.onCreate(savedInstanceState);

setContentView(R.layout.activity_upload);

txtPercentage = (TextView) findViewById(R.id.txtPercentage);

btnUpload = (Button) findViewById(R.id.btnUpload);

progressBar = (ProgressBar) findViewById(R.id.progressBar);

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

vidPreview = (VideoView) findViewById(R.id.videoPreview);

// Changing action bar background color

getActionBar().setBackgroundDrawable(                    new ColorDrawable(Color.parseColor(getResources().getString(

R.color.action_bar))));

// Receiving the data from previous activity

Intent i = getIntent();

// image or video path that is captured in previous activity

filePath = i.getStringExtra("filePath");

// boolean flag to identify the media type, image or video

boolean isImage = i.getBooleanExtra("isImage", true);

if (filePath != null) {                // Displaying the image or video on the screen

previewMedia(isImage);

} else {

Toast.makeText(getApplicationContext(),                        "Sorry, file path is missing!", Toast.LENGTH_LONG).show();

}

btnUpload.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {                    // uploading the file to server

new UploadFileToServer().execute();

}

});

}

/**

* Displaying captured image/video on the screen

* */

private void previewMedia(boolean isImage) {            // Checking whether captured media is image or video

if (isImage) {

imgPreview.setVisibility(View.VISIBLE);

vidPreview.setVisibility(View.GONE);                // bimatp factory

BitmapFactory.Options options = new BitmapFactory.Options();

// down sizing image as it throws OutOfMemory Exception for larger

// images

options.inSampleSize = 8;

final Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);

imgPreview.setImageBitmap(bitmap);

} else {

imgPreview.setVisibility(View.GONE);

vidPreview.setVisibility(View.VISIBLE);

vidPreview.setVideoPath(filePath);                // start playing

vidPreview.start();

}

}

/**

* Uploading the file to server

* */

private class UploadFileToServer extends AsyncTask {            @Override

protected void onPreExecute() {                // setting progress bar to zero

progressBar.setProgress(0);                super.onPreExecute();

}

@Override

protected void onProgressUpdate(Integer... progress) {                // Making progress bar visible

progressBar.setVisibility(View.VISIBLE);

// updating progress bar value

progressBar.setProgress(progress[0]);

// updating percentage value

txtPercentage.setText(String.valueOf(progress[0]) + "%");

}

@Override

protected String doInBackground(Void... params) {                return uploadFile();

}

@SuppressWarnings("deprecation")            private String uploadFile() {

String responseString = null;

HttpClient httpclient = new DefaultHttpClient();

HttpPost httppost = new HttpPost(Config.FILE_UPLOAD_URL);

try {

AndroidMultiPartEntity entity = new AndroidMultiPartEntity(                            new ProgressListener() {

@Override

public void transferred(long num) {

publishProgress((int) ((num / (float) totalSize) * 100));

}

});

File sourceFile = new File(filePath);

// Adding file data to http body

entity.addPart("image", new FileBody(sourceFile));

// Extra parameters if you want to pass to server

entity.addPart("website",                            new StringBody("www.androidhive.info"));

entity.addPart("email", new StringBody("abc@gmail.com"));

totalSize = entity.getContentLength();

httppost.setEntity(entity);

// Making server call

HttpResponse response = httpclient.execute(httppost);

HttpEntity r_entity = response.getEntity();

int statusCode = response.getStatusLine().getStatusCode();                    if (statusCode == 200) {                        // Server response

responseString = EntityUtils.toString(r_entity);

} else {

responseString = "Error occurred! Http Status Code: "

+ statusCode;

}

} catch (ClientProtocolException e) {

responseString = e.toString();

} catch (IOException e) {

responseString = e.toString();

}

return responseString;

}

@Override

protected void onPostExecute(String result) {

Log.e(TAG, "Response from server: " + result);

// showing the server response in an alert dialog

showAlert(result);

super.onPostExecute(result);

}

}

/**

* Method to show alert dialog

* */

private void showAlert(String message) {

AlertDialog.Builder builder = new AlertDialog.Builder(this);

builder.setMessage(message).setTitle("Response from Servers")

.setCancelable(false)

.setPositiveButton("OK", new DialogInterface.OnClickListener() {                        public void onClick(DialogInterface dialog, int id) {                            // do nothing

}

});

AlertDialog alert = builder.create();

alert.show();

}

}

到这里,我们已经把 Android 项目构建好了,现在让我们来快速地创建一个 PHP工程以用于接收我们 App 发送过来的文件,就能看到这个项目的实际想过啦。但在那之前,我们可能需要为 WAMP Server做一点点简单的配置。

2.安装与配置 WAMP Server

1、下载并安装 WAMP,在 Windows 系统中,WAMP 将会被安装在 C:wamp location。

2、打开 php,ini 文件,并修改下列值。在 WAMP Server 中,上传文件的最大值默认为 2MB,通过修改下列值,我们能够让上传文件的最大值达到 50MB。upload_max_filesize = 50M

post_max_size = 50M

max_input_time = 300

max_execution_time = 300

3、配置完成后重启 WAMP Server 即可。

3.创建一个 PHP 项目

1、到 C:wampwww 目录中创建一个名为 AndroidFileUpload 的文件夹,这将会是我们 PHP 项目的根目录

2、进入这个文件夹中创建一个叫作 uploads 的文件夹,用以储存所有上传到服务器的文件

3、创建一个叫做 fileUpload.php 的文件,并把下面的内容复制进去。这些 PHP 代码的作用是:接收我们 Android App 上传的文件,并把它们储存在 uploads 文件夹中。一旦上传完成,服务器就会返回一个 JSON。

fileUpload.php<?php

// Path to move uploaded files

$target_path = "uploads/";

// array for final json respone

$response = array();

// getting server ip address

$server_ip = gethostbyname(gethostname());

// final file url that is being uploaded

$file_upload_url = 'http://' . $server_ip . '/' . 'AndroidFileUpload' . '/' . $target_path;

if (isset($_FILES['image']['name'])) {

$target_path = $target_path . basename($_FILES['image']['name']);

// reading other post parameters

$email = isset($_POST['email']) ? $_POST['email'] : '';

$website = isset($_POST['website']) ? $_POST['website'] : '';

$response['file_name'] = basename($_FILES['image']['name']);

$response['email'] = $email;

$response['website'] = $website;

try {            // Throws exception incase file is not being moved

if (!move_uploaded_file($_FILES['image']['tmp_name'], $target_path)) {                // make error flag true

$response['error'] = true;

$response['message'] = 'Could not move the file!';

}

// File successfully uploaded

$response['message'] = 'File uploaded successfully!';

$response['error'] = false;

$response['file_path'] = $file_upload_url . basename($_FILES['image']['name']);

} catch (Exception $e) {            // Exception occurred. Make error flag true

$response['error'] = true;

$response['message'] = $e->getMessage();

}

} else {        // File parameter is missing

$response['error'] = true;

$response['message'] = 'Not received any file!F';

}

// Echo final json response to client

echo json_encode($response);    ?>

Below is the sample JSON response if the file is uploaded successfully. You can use error value to verify the upload on android side.

如果文件成功被上传,就会得到类似下面这个简单的 JSON 返回值范例。你可以使用错误代码来确认 Android 端的上传结果。{        "file_name": "DSC_0021.JPG",        "email": "admin@androidhive.info",        "website": "www.androidhive.info",        "message": "File uploaded successfully!",        "error": false,        "file_path": "http://192.168.0.104/AndroidFileUpload/uploads/DSC_0021.JPG"

}

4.测试上传文件功能(本地)

你可以通过完成下面的步骤在本地测试 App 的功能运行必要的设备(这里指的是运行 WAMP server 和 Android 手机),并将它们连在同一个 WIFI 网络中

开启 WAMP server

在电脑上运行 PHP 工程,并获取本机的 IP 地址。你可以通过在控制台中输入 ipconfig 命令获得你的 IP 地址。(在 Mac Os 系统中,你需要使用 ifconfig 获得 IP 地址)

把 Config 类中的 IP 地址换成你的 IP 地址(创建 Android 项目的第四步)

把我们的 Android App 安装到手机上,并运行它

引用Stackoverflow 上有一个相关的使用进度条上传文件的问题

我在 App 中使用的图标

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值