Android头像(图片)上传/文件上传

1、前言

作为初学者来说,想必对上传的数据格式肯定比较模糊,一般传递参数给后台,常用的是使用基本数据类型拼装成json格式。头像上传也可以使用String类型进行上传(极客学院有相关的视频,可以自行了解),本篇文章所讲的是使用文件的格式进行上传,也就是File类型。主要是实际的项目开发中,为了与web端的头像上传格式一致,所以一般使用File类型的还是比较多。之后会比较多的提到文件上传,这里就是指图片的上传,只是采用的是文件上传的格式。

2、效果图


3、实现步骤

1首先我们得获取相册中或者拍照所得到的照片,这个可以通过系统中的Intent获得

   2、 将得到的图片进行剪裁、压缩,然后根据图片路径转换成File文件

   3、 将File文件(这里指需要上传的图片)上传到服务器后台

<span style="background-color: rgb(153, 255, 153);"><span style="font-family: Arial, Helvetica, sans-serif;">注:这里上传到服务器的代码会比较复杂,需要拼接相应的Http协议。做过j2</span><span style="font-family: Arial, Helvetica, sans-serif;">ee的童鞋都知道,在jsp或html中上传文件非常简单,</span><span style="font-size: 14px; font-family: Arial, Helvetica, sans-serif;">一个form表单就可以搞定:</span></span>

<span style="white-space:pre">	</span><form action="/web/ManageServlet" method="post" enctype="multipart/form-data">
		文件标题:<input name="title" type="text"><br/>
		文件:    <input type="file" name="fileName"/>
		          <input type="submit" value=" 提 交 "/>
	</form>
之所以这么简便是因为浏览器会帮助我们处理相应的http协议,而在Android客户端中则需要我们自己去实现。下面我们先看一段浏览器向web服务器发送的http协议:



之后我们就会面向这段Htpp协议进行文件(图片)的上传


4、代码实现:


打开相机或者打开手机相册:

 <span style="white-space:pre">			</span>switch (position) {
                            case 0:
                                // 照相机
                                intent = new Intent("android.media.action.IMAGE_CAPTURE");
                                // 判断存储卡是否可以用,可用进行存储
                                if (hasSdcard()) {
                                    intent.putExtra(MediaStore.EXTRA_OUTPUT,
                                            Uri.fromFile(new File(Environment
                                                    .getExternalStorageDirectory(), PHOTO_FILE_NAME)));
                                }
                                startActivityForResult(intent, PHOTO_REQUEST_CAMERA);
                                break;
                            case 1:
                                // 图库选择
                                // 激活系统图库,选择一张图片
                                intent = new Intent(Intent.ACTION_PICK);
                                intent.setType("image/*");
                                startActivityForResult(intent, PHOTO_REQUEST_GALLERY);
                                break;
                            case 2:
                                //取消
                                break;
                        }

重写onActivityResult方法:

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

        if (requestCode == PHOTO_REQUEST_GALLERY) {//相册
            if (data != null) {
                // 得到图片的全路径
                Uri uri = data.getData();
                crop(uri);//剪切图片
            }

        } else if (requestCode == PHOTO_REQUEST_CAMERA) {//相机
            if (hasSdcard()) {
                tempFile = new File(Environment.getExternalStorageDirectory(),
                        PHOTO_FILE_NAME);
                crop(Uri.fromFile(tempFile));//剪切图片
            } else {
                showToast("未找到存储卡,无法存储照片!");
            }
            super.onActivityResult(requestCode, resultCode, data);
        }
    }

使用系统剪裁图片的方法:

  /**
     * 剪切图片
     *
     */
    private void crop(Uri uri) {
        // 裁剪图片意图
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        intent.putExtra("crop", "true");
        // 裁剪框的比例,1:1
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        // 裁剪后输出图片的尺寸大小
        intent.putExtra("outputX", 250);
        intent.putExtra("outputY", 250);
        // 图片格式
        intent.putExtra("outputFormat", "PNG");
        intent.putExtra("noFaceDetection", true);// 取消人脸识别
        intent.putExtra("return-data", true);// true:不返回uri,false:返回uri
        startActivityForResult(intent, PHOTO_REQUEST_CUT);//同样的在onActivityResult中处理剪裁好的图片
    }

 图片剪裁完成之后就可以把图片显示到图片控件中,并且在剪裁完成的回调中将图片上传到服务器


完整的onActivityResult:


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

        //修改昵称返回
        if (requestCode == REQ_CODE_UPDATE_NAME && resultCode == RESULT_OK) {
            tvUserName.setText(data.getStringExtra("name"));
        }

        if (requestCode == PHOTO_REQUEST_GALLERY) {//相册
            if (data != null) {
                // 得到图片的全路径
                Uri uri = data.getData();
                crop(uri);//剪切图片
            }

        } else if (requestCode == PHOTO_REQUEST_CAMERA) {//相机
            if (hasSdcard()) {
                tempFile = new File(Environment.getExternalStorageDirectory(),
                        PHOTO_FILE_NAME);
                crop(Uri.fromFile(tempFile));//剪切图片
            } else {
                showToast("未找到存储卡,无法存储照片!");
            }

        } else if (requestCode == PHOTO_REQUEST_CUT) {//图片剪裁完成
            try {
                bitmap = data.getParcelableExtra("data");
                sriUserPhoto.setImageBitmap(bitmap);//确定图片之后将显示在圆形控件上

                //pictureCutUtil是一个图片压缩的工具类
                String filePath = pictureCutUtil.cutPictureQuality(bitmap, Constant.PACKAGE_PICTURE);
                final File file = new File(filePath);
                //此处是需要传递到后台的参数
                int uid = getPreferenceHelper().getInt(Constant.DUOBAO_USER_UID, -1);
                final Map<String, String> map = new HashMap<String, String>();
                map.put("uid", uid + "");
                //开启子线程
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //文件上传, 参数解释见具体方法
                            uploadForm(map, "Filedata", file, null, HttpConstant.CLIENT_HOME_USERPHOTOUP);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }


图片压缩工具类:

/**
 * 图片压缩
 * 
 * @author lihaotian
 * 
 */
public class PictureCutUtil {

	private Context context;

	/**
	 * 构造器
	 * 
	 * @param context
	 */
	public PictureCutUtil(Context context) {
		this.context = context;
	}

	/**
	 * 计算图片的缩放值
	 * 
	 * @param options
	 * @param reqWidth
	 * @param reqHeight
	 * @return
	 */
	private int cutBySize(BitmapFactory.Options options, int reqWidth, int reqHeight) {

		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;

		if (height > reqHeight || width > reqWidth) {

			final int heightRatio = Math.round((float) height / (float) reqHeight);
			final int widthRatio = Math.round((float) width / (float) reqWidth);

			inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
		}

		return inSampleSize;
	}

	/**
	 * 根据路径获得图片并压缩返回bitmap
	 * 
	 * @param filePath
	 * @return
	 */
	public Bitmap cutPictureSize(String filePath) {

		Display mDisplay = ((Activity) context).getWindowManager().getDefaultDisplay();
		int width = mDisplay.getWidth();
		int height = mDisplay.getHeight();

		final BitmapFactory.Options options = new BitmapFactory.Options();
		options.inJustDecodeBounds = true;
		BitmapFactory.decodeFile(filePath, options);
		options.inSampleSize = cutBySize(options, width, height);
		options.inJustDecodeBounds = false;

		return BitmapFactory.decodeFile(filePath, options);
	}

	/**
	 * 存储图片
	 * 
	 * @param bitmap
	 * @param savePackage
	 * @return
	 */
	public String cutPictureQuality(Bitmap bitmap, String savePackage) {

		String fileName = UUID.randomUUID().toString().replace("-","") + ".png";
		String filePath = Environment.getExternalStorageDirectory() + File.separator + savePackage;

		// 判断文件夹存在
		File file = new File(filePath);
		if (file != null && !file.exists()) {
			file.mkdirs();
		}

		try {

			// 第一次压缩
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

			FileOutputStream fos = new FileOutputStream(new File(filePath, fileName));

			int options = 100;
			// 如果大于150kb则再次压缩,最多压缩三次
			while (baos.toByteArray().length / 1024 > 150 && options != 10) {
				// 清空baos
				baos.reset();
				// 这里压缩options%,把压缩后的数据存放到baos中
				bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);
				options -= 30;
			}
			fos.write(baos.toByteArray());
			fos.close();
			baos.close();

		} catch (Exception e) {
		}
		return filePath + File.separator + fileName;
	}
}


具体的图片上传(关键),本篇介绍两种上传的方法,第一种是HttpURLConnection,上面上传文件调用的就是此方法:

   /**
     *android上传文件到服务器
     * @param params:请求参数
     * @param fileFormName:文件名称
     * @param uploadFile:上传的文件
     * @param newFileName:可不写
     * @param urlStr:后台地址
     * @throws IOException
     */

    public void uploadForm(Map<String, String> params, String fileFormName,
                           File uploadFile, String newFileName, String urlStr)
            throws IOException {

        if (newFileName == null || newFileName.trim().equals("")) {
            newFileName = uploadFile.getName();
        }

        StringBuilder sb = new StringBuilder();
        /**
         * 普通的表单数据
         */
        if (params != null)
            for (String key : params.keySet()) {
                sb.append("--" + BOUNDARY + "\r\n");
                sb.append("Content-Disposition: form-data; name=\"" + key
                        + "\"" + "\r\n");
                sb.append("\r\n");
                sb.append(params.get(key) + "\r\n");
            }
        /**
         * 上传文件的头
         */
        sb.append("--" + BOUNDARY + "\r\n");
        sb.append("Content-Disposition: form-data; name=\"" + fileFormName
                + "\"; filename=\"" + newFileName + "\"" + "\r\n");
        sb.append("Content-Type: image/png" + "\r\n");// 如果服务器端有文件类型的校验,必须明确指定ContentType
        sb.append("\r\n");

        byte[] headerInfo = sb.toString().getBytes("UTF-8");
        byte[] endInfo = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("UTF-8");
        System.out.println(sb.toString());
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type",
                "multipart/form-data; boundary=" + BOUNDARY);
        conn.setRequestProperty("Content-Length", String
                .valueOf(headerInfo.length + uploadFile.length()
                        + endInfo.length));
        conn.setDoOutput(true);

        OutputStream out = conn.getOutputStream();
        InputStream in = new FileInputStream(uploadFile);
        out.write(headerInfo);

        byte[] buf = new byte[1024];
        int len;
        while ((len = in.read(buf)) != -1)
            out.write(buf, 0, len);

        out.write(endInfo);
        in.close();
        out.close();
        if (conn.getResponseCode() == 200) {
            System.out.println("上传成功");
        }
    }

第二种使用socket:(可作为了解) 网上有人说HttpURLConnection有一定缓存,容易造成内存溢出,具体的没有深究,就当我扯淡~~~

public class SocketHttpRequester {
	/**
	 * 直接通过HTTP协议提交数据到服务器,实现如下面表单提交功能:
	 *   <FORM METHOD=POST ACTION="http://192.168.0.200:8080/ssi/fileload/test.do" enctype="multipart/form-data">
	 <INPUT TYPE="text" NAME="name">
	 <INPUT TYPE="text" NAME="id">
	 <input type="file" name="imagefile"/>
	 <input type="file" name="zip"/>
	 </FORM>
	 * @param path 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试)
	 * @param params 请求参数 key为参数名,value为参数值
	 * @param files 上传文件
	 */
	public static boolean post(String path, Map<String, String> params, FormFile[] files) throws Exception{
		final String BOUNDARY = "---------------------------7da2137580612"; //数据分隔线
		final String endline = "--" + BOUNDARY + "--\r\n";//数据结束标志

		int fileDataLength = 0;
		for(FormFile uploadFile : files){//得到文件类型数据的总长度
			StringBuilder fileExplain = new StringBuilder();
			fileExplain.append("--");
			fileExplain.append(BOUNDARY);
			fileExplain.append("\r\n");
			fileExplain.append("Content-Disposition: form-data;name=\""+ uploadFile.getParameterName()+"\";filename=\""+ uploadFile.getFilname() + "\"\r\n");
			fileExplain.append("Content-Type: "+ uploadFile.getContentType()+"\r\n\r\n");
			fileDataLength += fileExplain.length();
			if(uploadFile.getInStream()!=null){
				fileDataLength += uploadFile.getFile().length();
			}else{
				fileDataLength += uploadFile.getData().length;
			}
			fileDataLength += "\r\n".length();
		}
		StringBuilder textEntity = new StringBuilder();
		for (Map.Entry<String, String> entry : params.entrySet()) {//构造文本类型参数的实体数据
			textEntity.append("--");
			textEntity.append(BOUNDARY);
			textEntity.append("\r\n");
			textEntity.append("Content-Disposition: form-data; name=\""+ entry.getKey() + "\"\r\n\r\n");
			textEntity.append(entry.getValue());
			textEntity.append("\r\n");
		}
		//计算传输给服务器的实体数据总长度
		int dataLength = textEntity.toString().getBytes().length + fileDataLength +  endline.getBytes().length;

		URL url = new URL(path);
		int port = url.getPort()==-1 ? 80 : url.getPort();
		Socket socket = new Socket(InetAddress.getByName(url.getHost()), port);
		OutputStream outStream = socket.getOutputStream();
		//下面完成HTTP请求头的发送
		String requestmethod = "POST "+ url.getPath()+" HTTP/1.1\r\n";
		outStream.write(requestmethod.getBytes());
		String accept = "Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n";
		outStream.write(accept.getBytes());
		String language = "Accept-Language: zh-CN\r\n";
		outStream.write(language.getBytes());
		String contenttype = "Content-Type: multipart/form-data; boundary="+ BOUNDARY+ "\r\n";
		outStream.write(contenttype.getBytes());
		String contentlength = "Content-Length: "+ dataLength + "\r\n";
		outStream.write(contentlength.getBytes());
		String alive = "Connection: Keep-Alive\r\n";
		outStream.write(alive.getBytes());
		String host = "Host: "+ url.getHost() +":"+ port +"\r\n";
		outStream.write(host.getBytes());
		//写完HTTP请求头后根据HTTP协议再写一个回车换行
		outStream.write("\r\n".getBytes());
		//把所有文本类型的实体数据发送出来
		outStream.write(textEntity.toString().getBytes());
		//把所有文件类型的实体数据发送出来
		for(FormFile uploadFile : files){
			StringBuilder fileEntity = new StringBuilder();
			fileEntity.append("--");
			fileEntity.append(BOUNDARY);
			fileEntity.append("\r\n");
			fileEntity.append("Content-Disposition: form-data;name=\""+ uploadFile.getParameterName()+"\";filename=\""+ uploadFile.getFilname() + "\"\r\n");
			fileEntity.append("Content-Type: "+ uploadFile.getContentType()+"\r\n\r\n");
			outStream.write(fileEntity.toString().getBytes());
			if(uploadFile.getInStream()!=null){
				byte[] buffer = new byte[1024];
				int len = 0;
				while((len = uploadFile.getInStream().read(buffer, 0, 1024))!=-1){
					outStream.write(buffer, 0, len);
				}
				uploadFile.getInStream().close();
			}else{
				outStream.write(uploadFile.getData(), 0, uploadFile.getData().length);
			}
			outStream.write("\r\n".getBytes());
		}
		//下面发送数据结束标志,表示数据已经结束
		outStream.write(endline.getBytes());
		BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//		JsonUtils.parseObject(convertStreamToString(socket.getInputStream()),);
		if(reader.readLine().indexOf("200")==-1){//读取web服务器返回的数据,判断请求码是否为200,如果不是200,代表请求失败
			return false;
		}
		outStream.flush();
		outStream.close();
		reader.close();
		socket.close();
		return true;
	}

	/**
	 * 临时
	 * @param is
	 * @return
	 */
	public static String convertStreamToString(InputStream is) {
		BufferedReader reader = new BufferedReader(new InputStreamReader(is));
		StringBuilder sb = new StringBuilder();

		String line = null;
		try {
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		return sb.toString();
	}

	/**
	 * 提交数据到服务器
	 * @param path 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试)
	 * @param params 请求参数 key为参数名,value为参数值
	 * @param file 上传文件
	 */
	public static boolean post(String path, Map<String, String> params, FormFile file) throws Exception{
		return post(path, params, new FormFile[]{file});
	}
	/**
	 * 提交数据到服务器
	 * @param path 上传路径(注:避免使用localhost或127.0.0.1这样的路径测试,因为它会指向手机模拟器,你可以使用http://www.itcast.cn或http://192.168.1.10:8080这样的路径测试)
	 * @param params 请求参数 key为参数名,value为参数值
	 * @param encode 编码
	 */
	public static byte[] postFromHttpClient(String path, Map<String, String> params, String encode) throws Exception{
		List<NameValuePair> formparams = new ArrayList<NameValuePair>();//用于存放请求参数
		for(Map.Entry<String, String> entry : params.entrySet()){
			formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}
		UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formparams, encode);
		HttpPost httppost = new HttpPost(path);
		httppost.setEntity(entity);
		HttpClient httpclient = new DefaultHttpClient();//看作是浏览器
		HttpResponse response = httpclient.execute(httppost);//发送post请求		
		return readStream(response.getEntity().getContent());
	}

	/**
	 * 发送请求
	 * @param path 请求路径
	 * @param params 请求参数 key为参数名称 value为参数值
	 * @param encode 请求参数的编码
	 */
	public static byte[] post(String path, Map<String, String> params, String encode) throws Exception{
		//String params = "method=save&name="+ URLEncoder.encode("老毕", "UTF-8")+ "&age=28&";//需要发送的参数
		StringBuilder parambuilder = new StringBuilder("");
		if(params!=null && !params.isEmpty()){
			for(Map.Entry<String, String> entry : params.entrySet()){
				parambuilder.append(entry.getKey()).append("=")
						.append(URLEncoder.encode(entry.getValue(), encode)).append("&");
			}
			parambuilder.deleteCharAt(parambuilder.length()-1);
		}
		byte[] data = parambuilder.toString().getBytes();
		URL url = new URL(path);
		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		conn.setDoOutput(true);//允许对外发送请求参数
		conn.setUseCaches(false);//不进行缓存
		conn.setConnectTimeout(5 * 1000);
		conn.setRequestMethod("POST");
		//下面设置http请求头
		conn.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*");
		conn.setRequestProperty("Accept-Language", "zh-CN");
		conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		conn.setRequestProperty("Content-Length", String.valueOf(data.length));
		conn.setRequestProperty("Connection", "Keep-Alive");

		//发送参数
		DataOutputStream outStream = new DataOutputStream(conn.getOutputStream());
		outStream.write(data);//把参数发送出去
		outStream.flush();
		outStream.close();
		if(conn.getResponseCode()==200){
			return readStream(conn.getInputStream());
		}
		return null;
	}

	/**
	 * 读取流
	 * @param inStream
	 * @return 字节数组
	 * @throws Exception
	 */
	public static byte[] readStream(InputStream inStream) throws Exception{
		ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = -1;
		while( (len=inStream.read(buffer)) != -1){
			outSteam.write(buffer, 0, len);
		}
		outSteam.close();
		inStream.close();
		return outSteam.toByteArray();
	}
}

/**
 * 上传文件
 */
public class FormFile {
	/* 上传文件的数据 */
	private byte[] data;
	private InputStream inStream;
	private File file;
	/* 文件名称 */
	private String filname;
	/* 请求参数名称*/
	private String parameterName;
	/* 内容类型 */
	private String contentType = "application/octet-stream";

	public FormFile(String filname, byte[] data, String parameterName, String contentType) {
		this.data = data;
		this.filname = filname;
		this.parameterName = parameterName;
		if(contentType!=null) this.contentType = contentType;
	}

	public FormFile(File file, String parameterName, String contentType) {
		this.filname = file.getName();
		this.parameterName = parameterName;
		this.file = file;
		try {
			this.inStream = new FileInputStream(file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		if(contentType!=null) this.contentType = contentType;
	}

	public File getFile() {
		return file;
	}

	public InputStream getInStream() {
		return inStream;
	}

	public byte[] getData() {
		return data;
	}

	public String getFilname() {
		return filname;
	}

	public void setFilname(String filname) {
		this.filname = filname;
	}

	public String getParameterName() {
		return parameterName;
	}

	public void setParameterName(String parameterName) {
		this.parameterName = parameterName;
	}

	public String getContentType() {
		return contentType;
	}

	public void setContentType(String contentType) {
		this.contentType = contentType;
	}

}


其实就这么简单,完工。。
















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值