目录
1 服务器接口简介
2 Android端代码实现
2.1 xml布局文件
2.2 Activity类
2.3 Okhttp网络通信类
1 服务器接口简介
此处我使用的服务器接口是使用Flask编写,具体实现代码:
# -*- coding: utf-8 -*- from flask import Flask, render_template, jsonify, request import time import os import base64 app = Flask(__name__) UPLOAD_FOLDER = 'E:\myupload\picture' app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER basedir = os.path.abspath(os.path.dirname(__file__)) ALLOWED_EXTENSIONS = set(['txt', 'png', 'jpg', 'xls', 'JPG', 'PNG', 'xlsx', 'gif', 'GIF','mp4']) # 用于判断文件后缀 def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS # 上传文件 @app.route('/api/upload', methods=['POST'], strict_slashes=False) def api_upload(): file_dir = os.path.join(basedir, app.config['UPLOAD_FOLDER']) if not os.path.exists(file_dir): os.makedirs(file_dir) f = request.files['myfile'] # 从表单的file字段获取文件,myfile为该表单的name值 if f and allowed_file(f.filename): # 判断是否是允许上传的文件类型 fname = f.filename print fname ext = fname.rsplit('.', 1)[1] # 获取文件后缀 unix_time = int(time.time()) new_filename = str(unix_time) + '.' + ext # 修改了上传的文件名 f.save(os.path.join(file_dir, new_filename)) # 保存文件到upload目录 print new_filename token = base64.b64encode(new_filename) print token return jsonify({"errno": 0, "errmsg": "上传成功", "token": token}) else: return jsonify({"errno": 1001, "errmsg": "上传失败"}) if __name__ == '__main__': app.run(debug=True)
参考文章:https://www.cnblogs.com/mosson/p/6163233.html
附加使用PostMan测试上传文件的操作方法:http://blog.csdn.net/u013420865/article/details/69665180
2 Android端代码实现
代码分三部分:
分别是xml布局文件,Activity类,和Okhttp网络通信类。
2.1 xml布局文件
activity_video_upload.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayout android:layout_width="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:layout_height="60dp" android:layout_marginTop="80dp" android:orientation="horizontal"> <TextView android:layout_width="match_parent" android:layout_height="50dp" android:layout_gravity="center_vertical" android:gravity="center" android:text="视频名称:" android:textColor="@color/black2" android:textSize="18dp"/> <EditText android:id="@+id/upload_video_name" android:layout_width="280dp" android:layout_height="50dp" android:layout_gravity="center_vertical" android:hint="请输入上传视频名称" android:layout_marginLeft="5dp" android:textSize="18dp" /> LinearLayout> <Button android:id="@+id/video_select" android:layout_width="match_parent" android:layout_height="44dp" android:layout_marginLeft="100dp" android:layout_marginRight="100dp" android:layout_marginTop="80dp" android:background="@drawable/exit_btn_blue" android:text="选择视频" android:textStyle="bold" android:textColor="@android:color/white" android:textSize="20sp"/> <Button android:id="@+id/video_upload" android:layout_width="match_parent" android:layout_height="44dp" android:layout_marginLeft="100dp" android:layout_marginRight="100dp" android:layout_marginTop="40dp" android:background="@drawable/exit_btn_blue" android:text="点击上传" android:textStyle="bold" android:textColor="@android:color/white" android:textSize="20sp"/> <TextView android:id="@+id/post_text" android:layout_marginTop="40dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="0" /> <ProgressBar android:id="@+id/post_progress" android:layout_marginLeft="20dp" android:layout_marginRight="20dp" style="?android:attr/progressBarStyleHorizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" /> LinearLayout>
2.2 Activity类
VideoUploadActivity类:
package com.liu.dance.video; import android.app.Activity; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.liu.dance.R; import com.liu.dance.util.HttpUtil; import com.liu.dance.util.ProgressListener; import java.io.File; import java.net.URI; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; public class VideoUploadActivity extends AppCompatActivity { public static final String TAG = VideoUploadActivity.class.getName(); public final static int VEDIO_KU = 101; private String path = "";//文件路径 private ProgressBar post_progress; private TextView post_text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_upload); getSupportActionBar().setTitle("视频上传"); getSupportActionBar().setDisplayHomeAsUpEnabled(true); final EditText video_name = (EditText)findViewById(R.id.upload_video_name); post_progress = (ProgressBar) findViewById(R.id.post_progress); post_text = (TextView) findViewById(R.id.post_text); findViewById(R.id.video_select).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { seleteVedio(); video_name.setText(path); } }); findViewById(R.id.video_upload).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // Toast.makeText(VideoUploadActivity.this, "路径:"+basePath, Toast.LENGTH_LONG).show(); if(path.equals("")) Toast.makeText(VideoUploadActivity.this, "请选择视频后,再点击上传!", Toast.LENGTH_LONG).show(); else { File file = new File( path); String postUrl = "http://120.79.82.151/api/upload"; HttpUtil.postFile(postUrl, new ProgressListener() { @Override public void onProgress(long currentBytes, long contentLength, boolean done) { Log.i(TAG, "currentBytes==" + currentBytes + "==contentLength==" + contentLength + "==done==" + done); int progress = (int) (currentBytes * 100 / contentLength); post_progress.setProgress(progress); post_text.setText(progress + "%"); } }, new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { if (response != null) { String result = response.body().string(); Log.i(TAG, "result===" + result); } } }, file); } } }); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); break; default: break; } return super.onOptionsItemSelected(item); } public void seleteVedio() { // TODO 启动相册 Intent intent = new Intent(); intent.setType("video/*"); intent.setAction(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); startActivityForResult(intent,VideoUploadActivity.VEDIO_KU); } /** * 选择回调 */ @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { // TODO 视频 case VideoUploadActivity.VEDIO_KU: if (resultCode == Activity.RESULT_OK) { try { Uri uri = data.getData(); uri = geturi(this, data); File file = null; if (uri.toString().indexOf("file") == 0) { file = new File(new URI(uri.toString())); path = file.getPath(); } else { path = getPath(uri); file = new File(path); } if (!file.exists()) { break; } if (file.length() > 100 * 1024 * 1024) { // "文件大于100M"; break; } //视频播放 // mVideoView.setVideoURI(uri); // mVideoView.start(); //开始上传视频, // submitVedio(); } catch (Exception e) { String a=e+""; } catch (OutOfMemoryError e) { String a=e+""; } } break; } } public static Uri geturi(Context context, android.content.Intent intent) { Uri uri = intent.getData(); String type = intent.getType(); if (uri.getScheme().equals("file") && (type.contains("image/"))) { String path = uri.getEncodedPath(); if (path != null) { path = Uri.decode(path); ContentResolver cr = context.getContentResolver(); StringBuffer buff = new StringBuffer(); buff.append("(").append(MediaStore.Images.ImageColumns.DATA).append("=") .append("'" + path + "'").append(")"); Cursor cur = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.ImageColumns._ID }, buff.toString(), null, null); int index = 0; for (cur.moveToFirst(); !cur.isAfterLast(); cur.moveToNext()) { index = cur.getColumnIndex(MediaStore.Images.ImageColumns._ID); // set _id value index = cur.getInt(index); } if (index == 0) { // do nothing } else { Uri uri_temp = Uri .parse("content://media/external/images/media/" + index); if (uri_temp != null) { uri = uri_temp; Log.i("urishi", uri.toString()); } } } } return uri; } private String getPath(Uri uri) { String[] projection = {MediaStore.Video.Media.DATA}; Cursor cursor = managedQuery(uri, projection, null, null, null); int column_index = cursor .getColumnIndexOrThrow(MediaStore.Video.Media.DATA); cursor.moveToFirst(); return cursor.getString(column_index); } }
2.3 Okhttp网络通信类
HttpUtil类:
package com.liu.dance.util; import android.util.Log; import java.io.File; import java.util.concurrent.TimeUnit; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; /** * Created by 舞动的心 on 2018/3/5. */ public class HttpUtil { private static OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(10000, TimeUnit.MILLISECONDS) .readTimeout(10000,TimeUnit.MILLISECONDS) .writeTimeout(10000, TimeUnit.MILLISECONDS).build(); public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");public static void postFile(String url, final ProgressListener listener, okhttp3.Callback callback, File...files){ MultipartBody.Builder builder = new MultipartBody.Builder(); builder.setType(MultipartBody.FORM); Log.i("huang","files[0].getName()=="+files[0].getName()); //第一个参数要与Servlet中的一致 builder.addFormDataPart("myfile",files[0].getName(), RequestBody.create(MediaType.parse("application/octet-stream"),files[0])); MultipartBody multipartBody = builder.build(); Request request = new Request.Builder().url(url).post(new ProgressRequestBody(multipartBody,listener)).build(); okHttpClient.newCall(request).enqueue(callback); } }
ProgressListener接口:
package com.liu.dance.util; /** * Created by 舞动的心 on 2018/3/8. */ public interface ProgressListener { void onProgress(long currentBytes, long contentLength, boolean done); }
ProgressModel类:
package com.liu.dance.util; import android.os.Parcel; import android.os.Parcelable; /** * Created by 舞动的心 on 2018/3/8. */ public class ProgressModel implements Parcelable { private long currentBytes; private long contentLength; private boolean done = false; public ProgressModel(long currentBytes, long contentLength, boolean done) { this.currentBytes = currentBytes; this.contentLength = contentLength; this.done = done; } public long getCurrentBytes() { return currentBytes; } public void setCurrentBytes(long currentBytes) { this.currentBytes = currentBytes; } public long getContentLength() { return contentLength; } public void setContentLength(long contentLength) { this.contentLength = contentLength; } public boolean isDone() { return done; } public void setDone(boolean done) { this.done = done; } private static final CreatorCREATOR = new Creator () { @Override public ProgressModel createFromParcel(Parcel parcel) { return new ProgressModel(parcel); } @Override public ProgressModel[] newArray(int i) { return new ProgressModel[i]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeLong(currentBytes); parcel.writeLong(contentLength); parcel.writeByte((byte) (done==true?1:0)); } protected ProgressModel(Parcel parcel) { currentBytes = parcel.readLong(); contentLength = parcel.readLong(); done = parcel.readByte()!=0; } }
ProgressRequestBody类:
package com.liu.dance.util; import android.os.Handler; import android.os.Looper; import android.os.Message; import java.io.IOException; import okhttp3.MediaType; import okhttp3.RequestBody; import okio.Buffer; import okio.BufferedSink; import okio.ForwardingSink; import okio.Okio; import okio.Sink; /** * Created by 舞动的心 on 2018/3/8. */ public class ProgressRequestBody extends RequestBody { public static final int UPDATE = 0x01; private RequestBody requestBody; private ProgressListener mListener; private BufferedSink bufferedSink; private MyHandler myHandler; public ProgressRequestBody(RequestBody body, ProgressListener listener) { requestBody = body; mListener = listener; if (myHandler==null){ myHandler = new MyHandler(); } } class MyHandler extends Handler { public MyHandler() { super(Looper.getMainLooper()); } @Override public void handleMessage(Message msg) { switch (msg.what){ case UPDATE: ProgressModel progressModel = (ProgressModel) msg.obj; if (mListener!=null)mListener.onProgress(progressModel.getCurrentBytes(),progressModel.getContentLength(),progressModel.isDone()); break; } } } @Override public MediaType contentType() { return requestBody.contentType(); } @Override public long contentLength() throws IOException { return requestBody.contentLength(); } @Override public void writeTo(BufferedSink sink) throws IOException { if (bufferedSink==null){ bufferedSink = Okio.buffer(sink(sink)); } //写入 requestBody.writeTo(bufferedSink); //刷新 bufferedSink.flush(); } private Sink sink(BufferedSink sink) { return new ForwardingSink(sink) { long bytesWritten = 0L; long contentLength = 0L; @Override public void write(Buffer source, long byteCount) throws IOException { super.write(source, byteCount); if (contentLength==0){ contentLength = contentLength(); } bytesWritten += byteCount; //回调 Message msg = Message.obtain(); msg.what = UPDATE; msg.obj = new ProgressModel(bytesWritten,contentLength,bytesWritten==contentLength); myHandler.sendMessage(msg); } }; } }
界面效果:
参考文章:http://blog.csdn.net/gary__123456/article/details/74157403