上传图片(用户头像)至服务器

  观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟。欢迎各位大佬来评论区提出问题或者是指出错误,分享宝贵经验。先谢谢了( ̄▽ ̄)"!

  前两期介绍了如何从服务器获取数据和加载图片,现在我们来看看如何把图片上传到服务器。这是一个很常见的需求,比如说上传用户头像等,本期也将围绕这个内容展开。首先服务器这边我们写一个上传图片的controller:

 package dolphin.controller;

 import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException; /**
* @description :数据更新控制层
* @author :郭小柒w
* @date :2020/5/16 16:03
* @version :1.0
*/
@Controller
public class UpdateController {
/**
*
* @param file
* @param request
* @return String 不同的返回值代表不同上传结果
* @throws IllegalStateException
* @throws IOException
*/
@RequestMapping( "/Upload")
@ResponseBody
public String photoUpload(MultipartFile file, HttpServletRequest request) throws IllegalStateException, IOException {
if (file != null) {// 判断上传的文件是否为空
String path = null;// 文件路径
String type = null;// 文件类型
String fileName = file.getOriginalFilename();// 文件原名称
System.out.println("上传的文件原名称:"+fileName);
// 判断文件类型
type = fileName.indexOf(".") != -1 ? fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()) : null;
if (type != null) {// 判断文件类型是否为空
if ("GIF".equals(type.toUpperCase()) || "PNG".equals(type.toUpperCase()) || "JPG".equals(type.toUpperCase())) {
// 项目在容器中实际发布运行的根路径
String realPath = request.getSession().getServletContext().getRealPath("/");
// 自定义的文件名称
String trueFileName = fileName;
// 设置存放图片文件的路径
path = realPath + "WEB-INF\\images\\head\\" + trueFileName;
// 转存文件到指定的路径
file.transferTo(new File(path));
System.out.println("文件成功上传到指定目录下");
}else {
System.out.println("不是我们想要的文件类型,请按要求重新上传");
return "1";
}
}else {
System.out.println("文件类型为空");
return "2";
}
}else {
System.out.println("没有找到相对应的文件");
return "3";
}
return "0";
}
}

  单单有这个还不够,我们还需要进行如下配置:

1.在pom.xml里加入上传文件相关的依赖(版本可根据需要进行更改):

 <!-- io包 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 文件上传组件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>

2.在springmvc.xml里新增如下配置:

 <!-- SpringMVC上传文件时,需要配置MultipartResolver处理器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="UTF-8" />
<!-- 指定所上传文件的总大小,单位字节。注意maxUploadSize属性的限制不是针对单个文件,而是所有文件的容量之和 -->
<property name="maxUploadSize" value="10240000" />
</bean>

然后是用于测试controller的页面,index.jsp(enctype="multipart/form-data"的作用是将form表单的数据以二进制的方式传输)

 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<fieldset>
<legend>图片上传</legend>
<h2>只能上传单张10M以下的 PNG、JPG、GIF 格式的图片</h2>
<form action="./Upload" method="post" enctype="multipart/form-data">
选择文件:<input type="file" name="file">
<input type="submit" value="上传">
</form>
</fieldset>
</body>
</html>

  我们先看一下效果,从本地选择一张符合要求图片:

  选择完毕后是这样:

  然后点击上传,页面跳转。上传成功的话页面只有一个“0”,图片不再贴出,我们先看一下控制台输出:

  从输出的存放路径找到我们上传的图片:

  从图中可以看出已经上传成功,证明我们的controller是可行的,接下来就是编写安卓端的代码,尝试从客户端上传图片。

  客户端获取图片大致就两种方式:1.拍照;2.本地图库。

  由于博主也不是什么技术大佬,所以老老实实地使用大神们造的轮子,主要是以下两个库:

  更详细的介绍请到相应网址查看,我这只是简单的使用,首先是页面布局:

 <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".UserHeadActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="30dp">
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="UserHeadActivity" />
<com.example.dolphin.utils.RoundImageView
android:id="@+id/image_view"
android:layout_marginTop="10dp"
android:layout_gravity="center_horizontal"
android:layout_width="150dp"
android:layout_height="150dp"
android:src="@drawable/ic_launcher"
/>
<Button
android:id="@+id/take_from_camera"
android:text="拍照"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/take_from_galley"
android:text="图库"
android:layout_gravity="center_horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

  可能你会有疑问,“com.example.dolphin.utils.RoundImageView”是个什么玩意儿?由于要做用户头像,我就顺便在网上找了一个自定义圆形ImageView控件的例子,非常简单,代码如下:

 package com.example.dolphin.utils;

 import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView; import androidx.annotation.Nullable; /**
* @author :created by 郭小柒w
* 时间 2020/5/16 15
* 自定义的圆形ImageView,可以直接当组件在布局中使用。
*/ @SuppressLint("AppCompatCustomView")
public class RoundImageView extends ImageView { //画笔
private Paint mPaint;
//圆形图片的半径
private int mRadius;
//图片的宿放比例
private float mScale; public RoundImageView(Context context) {
super(context);
} public RoundImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
} public RoundImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//由于是圆形,宽高应保持一致
int size = Math.min(getMeasuredWidth(), getMeasuredHeight());
mRadius = size / 2;
setMeasuredDimension(size, size);
} @SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) { mPaint = new Paint(); Drawable drawable = getDrawable(); if (null != drawable) {
Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap(); //初始化BitmapShader,传入bitmap对象
BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
//计算缩放比例
mScale = (mRadius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth()); Matrix matrix = new Matrix();
matrix.setScale(mScale, mScale);
bitmapShader.setLocalMatrix(matrix);
mPaint.setShader(bitmapShader);
//画圆形,指定好坐标,半径,画笔
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
} else {
super.onDraw(canvas);
}
} }

RoundImageView.java

  在你的项目里创建这个类,之后就可以像上边那样使用啦(使用时注意类的路径,不要直接复制粘贴上面的代码),最后是页面对应的Activity:

 package com.example.dolphin;

 import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast; import com.bumptech.glide.Glide;
import com.example.dolphin.utils.Constants;
import com.jph.takephoto.app.TakePhoto;
import com.jph.takephoto.app.TakePhotoActivity;
import com.jph.takephoto.compress.CompressConfig;
import com.jph.takephoto.model.CropOptions;
import com.jph.takephoto.model.TResult;
import com.yanzhenjie.permission.AndPermission;
import com.yanzhenjie.permission.PermissionListener;
import com.zhy.http.okhttp.OkHttpUtils;
import com.zhy.http.okhttp.callback.StringCallback; import java.io.File;
import java.util.List; import okhttp3.Call; public class UserHeadActivity extends TakePhotoActivity { //UIs
private Button takeFromCameraBtn, takeFromGalleyBtn; //拍照以及从相册中选取Button
private ImageView imageView; //图片展示ImageView //TakePhoto
private TakePhoto takePhoto;
private CropOptions cropOptions; //裁剪参数
private CompressConfig compressConfig; //压缩参数
private Uri imageUri; //图片保存路径 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_userhead);
//申请相关权限
initPermission();
//设置压缩、裁剪参数
initData();
takeFromCameraBtn = (Button) findViewById(R.id.take_from_camera);
takeFromCameraBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
imageUri = getImageCropUri();
//拍照并裁剪
takePhoto.onPickFromCaptureWithCrop(imageUri, cropOptions);
//仅仅拍照不裁剪
//takePhoto.onPickFromCapture(imageUri);
}
}); takeFromGalleyBtn = (Button) findViewById(R.id.take_from_galley);
takeFromGalleyBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
imageUri = getImageCropUri();
//从相册中选取图片并裁剪
takePhoto.onPickFromGalleryWithCrop(imageUri, cropOptions);
//从相册中选取不裁剪
//takePhoto.onPickFromGallery();
}
}); imageView = (ImageView) findViewById(R.id.image_view);
} @Override
public void takeSuccess(TResult result) {
super.takeSuccess(result);
String iconPath = result.getImage().getOriginalPath();
//Toast显示图片路径
Toast.makeText(this, "imagePath:" + iconPath, Toast.LENGTH_SHORT).show();
//上传图片
submitHead(iconPath);
//Google Glide库 用于加载图片资源,这里是把图片展示在页面上
Glide.with(this).load(iconPath).asBitmap().into(imageView);
} @Override
public void takeFail(TResult result, String msg) {
super.takeFail(result, msg);
Toast.makeText(UserHeadActivity.this, "Error:" + msg, Toast.LENGTH_SHORT).show();
} @Override
public void takeCancel() {
super.takeCancel();
} private void initPermission() {
// 申请权限。
AndPermission.with(this)
.requestCode(100)
.permission(Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE)
.send();
} @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// 只需要调用这一句,其它的交给AndPermission吧,最后一个参数是PermissionListener。
AndPermission.onRequestPermissionsResult(requestCode, permissions, grantResults, listener);
} //权限申请回调接口
private PermissionListener listener = new PermissionListener() {
@Override
public void onSucceed(int requestCode, List<String> grantedPermissions) {
// 权限申请成功回调。
if(requestCode == 100) {
// TODO 相应代码。
//do nothing
}
}
@Override
public void onFailed(int requestCode, List<String> deniedPermissions) {
// 权限申请失败回调。 // 用户否勾选了不再提示并且拒绝了权限,那么提示用户到设置中授权。
if (AndPermission.hasAlwaysDeniedPermission(UserHeadActivity.this, deniedPermissions)) { // 用自定义的提示语
AndPermission.defaultSettingDialog(UserHeadActivity.this, 103)
.setTitle("权限申请失败")
.setMessage("我们需要的一些权限被您拒绝或者系统发生错误申请失败,请您到设置页面手动授权,否则功能无法正常使用!")
.setPositiveButton("好,去设置")
.show();
}
}
}; private void initData() {
////获取TakePhoto实例
takePhoto = getTakePhoto();
//设置裁剪参数
cropOptions = new CropOptions.Builder().setAspectX(1).setAspectY(1).setWithOwnCrop(false).create();
//设置压缩参数
compressConfig=new CompressConfig.Builder().setMaxSize(50*1024).setMaxPixel(800).create();
takePhoto.onEnableCompress(compressConfig,true); //设置为需要压缩
} //获得照片的输出保存Uri
private Uri getImageCropUri() {
File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis()+".jpg");
if (!file.getParentFile().exists())
file.getParentFile().mkdirs();
return Uri.fromFile(file);
} private void submitHead(String iconPath){
//获取对应图片
File file = new File(iconPath);
//设置网络请求路径
String url = Constants.BASE_URL+"/Upload";
OkHttpUtils.post().url(url)
.addFile("file",file.getName(),file)
.build()
.execute(new StringCallback() {
@Override
public void onError(Call call, Exception e, int id) {
Toast.makeText(UserHeadActivity.this,"网络异常,请稍后再试",Toast.LENGTH_SHORT).show();
System.out.println("页面请求失败=="+e.getMessage());
} @Override
public void onResponse(String response, int id) {
System.out.println("首页请求成功=="+response);
ResultOfUpload(response);
}
});
}
//对返回结果进行处理
private void ResultOfUpload(String code){
if(code.equals("0"))
Toast.makeText(this,"上传成功",Toast.LENGTH_SHORT).show();
else if(code.equals("1"))
Toast.makeText(this,"文件格式不符,请重新上传",Toast.LENGTH_SHORT).show();
else if(code.equals("2"))
Toast.makeText(this,"文件类型为空",Toast.LENGTH_SHORT).show();
else
Toast.makeText(this,"未找到对应文件",Toast.LENGTH_SHORT).show();
}
}

  所有工作都已完成,接下来看看实际效果如何。下面是调试时的录屏,这里只给出从图库获取并上传的示例,拍照的模块大家可以自行测试,我测试时没问题(录屏转gif还挺麻烦,画质有点糊,各位凑合着看吧

如何搭建一个WEB服务器项目(六)—— 上传图片至服务器的更多相关文章

  1. 搭建一个Web API项目(DDD)

    传送阵:写在最后 一.创建一个能跑的起来的Web API项目 1.建一个空的 ASP.NET Web应用 (为什么不直接添加一个Web API项目呢,那样会有些多余的内容(如js.css.Areas等 ...

  2. 如何搭建一个WEB服务器项目(二)—— 对数据库表进行基本的增删改查操作

    使用HibernateTemplate进行增删改查操作 观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟.欢迎各位大佬来评论区提出问题或者是指出 ...

  3. Spring Boot(一):如何使用Spring Boot搭建一个Web应用

    Spring Boot Spring Boot 是Spring团队旗下的一款Web 应用框架 其优势可以更快速的搭建一个Web应用 从根本上上来讲 Spring Boot并不是什么新的框架技术 而是在 ...

  4. asp.netmvc 三层搭建一个完整的项目

    接下来用 asp.net mvc 三层搭建一个完整的项目: 架构图: 使用的数据库: 一张公司的员工信息表,测试数据 解决方案项目设计: 1.新建一个空白解决方案名称为Company 2.在该解决方案 ...

  5. 从零开始的Spring Boot(1、搭建一个Spring Boot项目Hello World)

    搭建一个Spring Boot项目Hello World 写在前面 从零开始的Spring Boot(2.在Spring Boot中整合Servlet.Filter.Listener的方式):http ...

  6. 搭建一个web服务下载HDFS的文件

    需求描述 为了能方便快速的获取HDFS中的文件,简单的搭建一个web服务提供下载很方便快速,而且在web服务器端不留临时文件,只做stream中转,效率相当高! 使用的框架是SpringMVC+HDF ...

  7. 搭建一个Web Server站点

    题:搭建一个Web Server站点.安装web服务,并在本地创建index.html测试 1.安装http服务 yum -y install httpd 2.进入网站目录 cd /var/www/h ...

  8. [转载] IIS来搭建一个只能实现基本功能的FTP服务器

    转自  http://blog.sina.com.cn/s/blog_3f7e47f20100haur.html 本文介绍通过win7自带的IIS来搭建一个只能实现基本功能的FTP服务器,第一次装好W ...

  9. Spring框架——事务管理方式搭建一个小的项目

    学习Spring框架,通过事务管理的方式搭建一个小的项目,该项目可以查询对数据库中的图书库存数量进行修改. 首先,使用MVC分层的设计模式思想搭建项目目录结构. 此部分代码源码之中都有相关注释,所以尽 ...

  10. 如何搭建一个WEB服务器项目(四)—— 实现安卓端图片加载

    使用Glide安卓图片加载库 观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟.欢迎各位大佬来评论区提出问题或者是指出错误,分享宝贵经验.先谢谢 ...

随机推荐

  1. 用SQL查询分析实现类似金蝶K3的收发存明细表

    使用SQL查询分析实现类收发存的报表,原始需求在 另外一篇文章 的第四部分.下图是实现需求. 一.准备 删除临时表 [buy]判断是否存在临时表,存在则删除[/buy] if OBJECT_ID('t ...

  2. python 携程asyncio实现高并发示例1

    import asyncio #携程(携程不是函数) async def print_hello(): while True: print("hello world") await ...

  3. Java 动态编译--DynamicCompiler

    java 动态编译自己写过程的机会比较少,记录一下: package com.xzlf.dynamicCompile; import java.io.IOException; import java. ...

  4. CSS选择器与CSS的继承,层叠和特殊性

    什么是选择器?选择器{样式;},在{}之前的部分就是"选择器","选择器"指明了{}中的"样式"的作用对象,也就是"样式" ...

  5. appium同时运行两台真机

    执行命令: appium -p 4494 -bp 2253 -U GWY0217207001793 appium -p 4493 -bp 2252 -U 77fdaabc server 设置:http ...

  6. php下载各种编辑器输出的内容到word中展示

    <?php/** * Created by PhpStorm. * User: 工作 * Date: 2018/1/11 * Time: 12:02 */ //连接数据库$dsn = " ...

  7. redis的5种数据类型

    卸载服务:redis-server --service-uninstall 开启服务:redis-server --service-start 停止服务:redis-server --service- ...

  8. C++操作Kafka使用Protobuf进行跨语言数据交互

    C++操作Kafka使用Protobuf进行跨语言数据交互 Kafka 是一种分布式的,基于发布 / 订阅的消息系统.主要设计目标如下: 以时间复杂度为 O(1) 的方式提供消息持久化能力,即使对 T ...

  9. 短视频sdk:选择一个靠谱的短视频SDK 你需要了解这些

    2017 年,短视频成为了内容创业的新风口,各种短视频 App 如雨后春笋般先后上线.随着互联网内容消费升级,视频越来越像文字.图片一样,成为每一个 App 不可或缺的一部分. 为了能够更好地聚焦于业 ...

  10. handlebars模板引擎使用初探1

    谈到handlebars,我们不禁产生疑问,为什么要使用这样的一个工具呢?它究竟能为我们带来什么样的好处?如何使用它呢? 一.handlebars可以干什么? 首先,我们来看一个案例: 有这样的htm ...