今天主要完成的任務:客戶端上傳文件到服務器
遇到的困難:圖片上傳的數量不確定,有時會爲空,而okhttputils框架不能上傳空文件
解決辦法:判斷文件是否爲空,並採用HashMao方式進行圖片的儲存
源程序代碼:
Record.java
package com.itheima.network.fragment; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.Button; import android.widget.EditText; import android.widget.GridView; import android.widget.ImageView; import android.widget.RadioGroup; import android.widget.SimpleAdapter; import android.widget.SimpleAdapter.ViewBinder; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import com.itheima.network.R; import com.itheima.network.func_reference.MyDialog; import com.itheima.network.store.FindUserId; import com.jph.takephoto.app.TakePhoto; import com.jph.takephoto.app.TakePhotoImpl; import com.jph.takephoto.compress.CompressConfig; import com.jph.takephoto.model.CropOptions; import com.jph.takephoto.model.InvokeParam; import com.jph.takephoto.model.TContextWrap; import com.jph.takephoto.model.TResult; import com.jph.takephoto.permission.InvokeListener; import com.jph.takephoto.permission.PermissionManager; import com.jph.takephoto.permission.TakePhotoInvocationHandler; import com.zhy.http.okhttp.OkHttpUtils; import com.zhy.http.okhttp.callback.StringCallback; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Set; import okhttp3.Call; public class Record extends Fragment implements MyDialog.OnButtonClickListener, AdapterView.OnItemClickListener, TakePhoto.TakeResultListener, InvokeListener { private MyDialog dialog;// 圖片選擇對話框 public static final int NONE = 0; public static final int PHOTOHRAPH = 1;// 拍照 public static final int PHOTOZOOM = 2; // 縮放 public static final int PHOTORESOULT = 3;// 結果 public static final String IMAGE_UNSPECIFIED = "image/*"; private GridView gridView; // 網格顯示縮略圖 private final int IMAGE_OPEN = 4; // 打開圖片標記 private String pathImage; // 選擇圖片路徑 private Bitmap bmp; // 導入臨時圖片 private ArrayList<HashMap<String, Bitmap>> imageItem; private SimpleAdapter simpleAdapter; // 適配器 private EditText note; // 筆記文本 private EditText title; // 標題文本 private EditText kemu; //學科 private Button handin; // 上傳按鈕 private RadioGroup rg; // 單選按鈕組 private boolean judge; // 判定公開/私有 private View mview; private Context mContext; TakePhoto takePhoto; InvokeParam invokeParam; String imagePath; File file; Uri uri; int size; CropOptions cropOptions; Bitmap bitmap; File test; @SuppressLint("SourceLockedOrientationActivity") @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=View.inflate(getActivity(), R.layout.fragment_record,null); getTakePhoto().onCreate(savedInstanceState); mview=view; mContext=view.getContext(); //上傳文件到服務器中 init(); //加載圖片與回顯 initData(); initEvents(); return view; } public void initEvents() { //各控件初始化 file = new File(mContext.getExternalCacheDir(), System.currentTimeMillis() + ".png"); uri = Uri.fromFile(file); size = Math.min(getResources().getDisplayMetrics().widthPixels, getResources().getDisplayMetrics().heightPixels); cropOptions = new CropOptions.Builder().setOutputX(size).setOutputX(size).setWithOwnCrop(false).create(); } private void init() { //聲明 note = mview.findViewById(R.id.Wnote); gridView= mview.findViewById(R.id.gridView); handin = mview.findViewById(R.id.handin); title = mview.findViewById(R.id.title); kemu = mview.findViewById(R.id.kemu); rg = mview.findViewById(R.id.rg); note.setHorizontallyScrolling(true); gridView.setOnItemClickListener(this); dialog = new MyDialog(this.getActivity()); dialog.setOnButtonClickListener(this); // activity中調用其他activity中組件的方法 LayoutInflater layout = this.getLayoutInflater(); View view = layout.inflate(R.layout.layout_select_photo, null); //單選按鈕(公開/私有) rg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { @Override public void onCheckedChanged(RadioGroup group, int checkedId) { if(checkedId == R.id.ok){ judge = true; }else{ judge = false; } } }); handin.setOnClickListener(new View.OnClickListener() { //是否公開 private String see_str = ""; //正文 private String note_str = ""; //標題 private String title_str = ""; //學科 private String kemu_str = ""; //年級(傳參獲取) private String year_str = "3"; FindUserId findUserId=new FindUserId(getActivity()); String user_str=findUserId.ReadUserName(); int userID=findUserId.ReadUserId(); @Override public void onClick(View v) { Map<String,File> fileMap=new HashMap<>(); if(judge){ see_str = "public"; }else{ see_str = "private"; } note_str = note.getText().toString(); title_str = title.getText().toString(); kemu_str = kemu.getText().toString(); //圖片文件,設定最多上傳3張,不包括+號圖片。 int pic_i=0; for(HashMap<String, Bitmap> pic:imageItem){ Set<String> set=pic.keySet(); for(String key:set){ if(pic_i==0){ //跳過默認的+號圖片 pic_i++; continue; } //取出bitmap,轉換成file,上傳 else if(pic_i==1){ Bitmap fbm1 = pic.get(key); //設置文件命名前綴部分 String image1user = user_str+"_image1"; //調用方法生成圖片文件 File mFile1 = doImage(fbm1,image1user); fileMap.put("image1.png",mFile1); pic_i++; } else if(pic_i==2){ Bitmap fbm2 = pic.get(key); //設置文件命名前綴部分 String image2user = user_str+"_image2"; //調用方法生成圖片文件 File mFile2 = doImage(fbm2,image2user); fileMap.put("image2.png",mFile2); pic_i++; } else if(pic_i==3){ Bitmap fbm3 = pic.get(key); //設置文件命名前綴部分 String image3user = user_str+"_image3"; //調用方法生成圖片文件 File mFile3= doImage(fbm3,image3user); fileMap.put("image3.png",mFile3); pic_i++; } } } String URL="http://39.101.190.190:8080/CloudNoteServlet/UserServlet"+"?method=uploading&title="+title_str+ "&see="+see_str+"&kemu="+kemu_str+"&userID="+userID; String noteuser = user_str+"_note"; File Notefile = doString(note_str,noteuser); fileMap.put("note.txt",Notefile); Map<String, String> params = new HashMap<>(); params.put("username",user_str); OkHttpUtils.post() .files("files",fileMap) .url(URL) .params(params) .build() .connTimeOut(20000) .writeTimeOut(20000) .readTimeOut(20000) .execute(new StringCallback( ) { @Override public void onError(Call call, Exception e, int id) { Toast.makeText(getActivity(),"上傳文件出現異常,請重試"+id,Toast.LENGTH_SHORT).show(); } @Override public void onResponse(String response, int id) { if(response.equals("fail")) { Toast.makeText(getActivity(),"上傳文件失敗"+id,Toast.LENGTH_SHORT).show(); }else{ Toast.makeText(getActivity(),"上傳文件成功"+id,Toast.LENGTH_SHORT).show(); } } }); } }); } private void initData() { /* * 載入默認圖片添加圖片加號 */ bmp = BitmapFactory.decodeResource(getResources(), R.drawable.ic_addpic); // 加號 imageItem = new ArrayList<HashMap<String, Bitmap>>(); HashMap<String, Bitmap> map = new HashMap<String, Bitmap>(); map.put("itemImage", bmp); imageItem.add(map); simpleAdapter = new SimpleAdapter(this.getActivity(), imageItem, R.layout.griditem_addpic, new String[] { "itemImage" }, new int[] { R.id.imageView1 }); simpleAdapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { // TODO Auto-generated method stub if (view instanceof ImageView && data instanceof Bitmap) { ImageView i = (ImageView) view; i.setImageBitmap((Bitmap) data); return true; } return false; } }); gridView.setAdapter(simpleAdapter); } //Bitmap轉file public File doImage(Bitmap fbitmap, String user) { ByteArrayOutputStream fbaos = new ByteArrayOutputStream(); fbitmap.compress(Bitmap.CompressFormat.JPEG,100,fbaos); int options = 100; //判斷是否大於20kb,是則繼續壓縮 while(fbaos.toByteArray().length/1024>20 &&options>10){ fbaos.reset(); options-=10; fbitmap.compress(Bitmap.CompressFormat.JPEG,options,fbaos); long length = fbaos.toByteArray().length; } SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); Date date = new Date(System.currentTimeMillis()); String filename = user+"_"+format.format(date); String filepath = mContext.getExternalFilesDir(null).toString()+"/CloudNoteImage"; File pathfile = new File(filepath); if(!pathfile.exists()){ pathfile.mkdir(); } File file = new File(filepath,filename+".png"); //注意創建文件,否則會發生文件讀取錯誤 if(!file.exists()){ try{ file.createNewFile(); }catch (IOException e){ e.printStackTrace(); } } try{ FileOutputStream fos = new FileOutputStream(file); try{ fos.write(fbaos.toByteArray()); fos.flush(); fos.close(); } catch (IOException e) { e.printStackTrace(); } }catch (IOException e){ e.printStackTrace(); } return file; } //將正文信息寫入txt文件 public File doString(String strwrite, String user){ //取時間 SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); Date date = new Date(System.currentTimeMillis()); //生成文件名 String filename = user+"_"+format.format(date); String filepath =mContext.getExternalFilesDir(null).toString()+"/CloudNoteTXT"; //創建文件 File pathfile = new File(filepath); if(!pathfile.exists()){ pathfile.mkdir(); } File file = new File(filepath,filename+".txt"); if(!file.exists()){ try{ file.createNewFile(); }catch (IOException e){ e.printStackTrace(); } } try{ FileOutputStream outputStream = new FileOutputStream(file); outputStream.write(strwrite.getBytes()); outputStream.close(); } catch (IOException e) { e.printStackTrace(); } return file; } //調用相機 @Override public void camera() { takePhoto.onPickFromCaptureWithCrop(uri, cropOptions); } //調用相冊 @Override public void gallery() { takePhoto.onPickFromGalleryWithCrop(uri, cropOptions); } @Override public void cancel() { // TODO Auto-generated method stub dialog.cancel(); } @Override public void onResume() { // TODO Auto-generated method stub super.onResume(); //判定有圖片添加 if (!TextUtils.isEmpty(pathImage)) { //bitmap回顯 Bitmap addbmp = BitmapFactory.decodeFile(pathImage); //將信息存入Map中 HashMap<String, Bitmap> map = new HashMap<String, Bitmap>(); map.put("itemImage", addbmp); imageItem.add(map); //在griditem_addpic.xml中向imageView1添加圖片 simpleAdapter = new SimpleAdapter(this.getActivity(), imageItem, R.layout.griditem_addpic, new String[] { "itemImage" }, new int[] { R.id.imageView1 }); simpleAdapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { // TODO Auto-generated method stub if (view instanceof ImageView && data instanceof Bitmap) { ImageView i = (ImageView) view; i.setImageBitmap((Bitmap) data); return true; } return false; } }); gridView.setAdapter(simpleAdapter); simpleAdapter.notifyDataSetChanged(); // 刷新後釋放防止手機休眠後自動添加 pathImage = null; dialog.dismiss(); } } @Override public void onItemClick(AdapterView<?> parent, View v, int position, long id) { // TODO Auto-generated method stub if (imageItem.size() == 4&&position==0) { // 第一張爲默認圖片,點擊+號時才判定是否已滿 Toast.makeText(this.getActivity(), "圖片數3張已滿", Toast.LENGTH_SHORT).show(); } else if (position == 0) { // 點擊圖片位置爲+ 0對應0張圖片 // 選擇圖片 dialog.show(); // 通過onResume()刷新數據 } else { dialog(position); } } /* * Dialog對話框提示用戶刪除操作 position爲刪除圖片位置 */ protected void dialog(final int position) { AlertDialog.Builder builder = new Builder(this.getActivity()); builder.setMessage("確認移除已添加圖片嗎?"); builder.setTitle("提示"); builder.setPositiveButton("確認", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); imageItem.remove(position); simpleAdapter.notifyDataSetChanged(); } }); builder.setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); builder.create().show(); } @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { getTakePhoto().onActivityResult(requestCode, resultCode, data); super.onActivityResult(requestCode, resultCode, data); } @Override public void onSaveInstanceState(@NonNull Bundle outState) { getTakePhoto().onSaveInstanceState(outState); super.onSaveInstanceState(outState); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); //以下代碼爲處理Android6.0、7.0動態權限所需 PermissionManager.TPermissionType type = PermissionManager.onRequestPermissionsResult(requestCode, permissions, grantResults); PermissionManager.handlePermissionsResult(getActivity(), type, invokeParam, this); } @Override public PermissionManager.TPermissionType invoke(InvokeParam invokeParam) { PermissionManager.TPermissionType type = PermissionManager.checkPermission(TContextWrap.of(this), invokeParam.getMethod()); if (PermissionManager.TPermissionType.WAIT.equals(type)) { this.invokeParam = invokeParam; } return type; } public TakePhoto getTakePhoto() { //獲得TakePhoto實例 if (takePhoto == null) { takePhoto = (TakePhoto) TakePhotoInvocationHandler.of(this).bind(new TakePhotoImpl(this, this)); } //設置壓縮規則,最大500kb takePhoto.onEnableCompress(new CompressConfig.Builder().setMaxSize(500 * 1024).create(), true); return takePhoto; } @Override public void takeSuccess(final TResult result) { //成功取得照片 test = new File(result.getImage().getOriginalPath()); if(test.exists()){ bitmap = BitmapFactory.decodeFile(result.getImage().getOriginalPath()); // 將圖片放入gridview中 HashMap<String, Bitmap> map = new HashMap<String, Bitmap>(); map.put("itemImage", bitmap); imageItem.add(map); simpleAdapter = new SimpleAdapter(this.getActivity(), imageItem, R.layout.griditem_addpic, new String[] { "itemImage" }, new int[] { R.id.imageView1 }); simpleAdapter.setViewBinder(new ViewBinder() { @Override public boolean setViewValue(View view, Object data, String textRepresentation) { // TODO Auto-generated method stub if (view instanceof ImageView && data instanceof Bitmap) { ImageView i = (ImageView) view; i.setImageBitmap((Bitmap) data); return true; } return false; } }); gridView.setAdapter(simpleAdapter); simpleAdapter.notifyDataSetChanged(); dialog.dismiss(); } } @Override public void takeFail(TResult result, String msg) { //取得失敗 Toast.makeText(mContext,"設置失敗",Toast.LENGTH_SHORT).show(); } @Override public void takeCancel() { //取消 } }
運行截圖: