如何搭建一个WEB服务器项目(六)—— 上传图片至服务器
上传图片(用户头像)至服务器
观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟。欢迎各位大佬来评论区提出问题或者是指出错误,分享宝贵经验。先谢谢了( ̄▽ ̄)"!
前两期介绍了如何从服务器获取数据和加载图片,现在我们来看看如何把图片上传到服务器。这是一个很常见的需求,比如说上传用户头像等,本期也将围绕这个内容展开。首先服务器这边我们写一个上传图片的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.本地图库。
由于博主也不是什么技术大佬,所以老老实实地使用大神们造的轮子,主要是以下两个库:
- TakePhoto:https://github.com/crazycodeboy/TakePhoto
- AndPermission:https://github.com/yanzhenjie/AndPermission
更详细的介绍请到相应网址查看,我这只是简单的使用,首先是页面布局:
<?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服务器项目(六)—— 上传图片至服务器的更多相关文章
- 搭建一个Web API项目(DDD)
传送阵:写在最后 一.创建一个能跑的起来的Web API项目 1.建一个空的 ASP.NET Web应用 (为什么不直接添加一个Web API项目呢,那样会有些多余的内容(如js.css.Areas等 ...
- 如何搭建一个WEB服务器项目(二)—— 对数据库表进行基本的增删改查操作
使用HibernateTemplate进行增删改查操作 观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟.欢迎各位大佬来评论区提出问题或者是指出 ...
- Spring Boot(一):如何使用Spring Boot搭建一个Web应用
Spring Boot Spring Boot 是Spring团队旗下的一款Web 应用框架 其优势可以更快速的搭建一个Web应用 从根本上上来讲 Spring Boot并不是什么新的框架技术 而是在 ...
- asp.netmvc 三层搭建一个完整的项目
接下来用 asp.net mvc 三层搭建一个完整的项目: 架构图: 使用的数据库: 一张公司的员工信息表,测试数据 解决方案项目设计: 1.新建一个空白解决方案名称为Company 2.在该解决方案 ...
- 从零开始的Spring Boot(1、搭建一个Spring Boot项目Hello World)
搭建一个Spring Boot项目Hello World 写在前面 从零开始的Spring Boot(2.在Spring Boot中整合Servlet.Filter.Listener的方式):http ...
- 搭建一个web服务下载HDFS的文件
需求描述 为了能方便快速的获取HDFS中的文件,简单的搭建一个web服务提供下载很方便快速,而且在web服务器端不留临时文件,只做stream中转,效率相当高! 使用的框架是SpringMVC+HDF ...
- 搭建一个Web Server站点
题:搭建一个Web Server站点.安装web服务,并在本地创建index.html测试 1.安装http服务 yum -y install httpd 2.进入网站目录 cd /var/www/h ...
- [转载] IIS来搭建一个只能实现基本功能的FTP服务器
转自 http://blog.sina.com.cn/s/blog_3f7e47f20100haur.html 本文介绍通过win7自带的IIS来搭建一个只能实现基本功能的FTP服务器,第一次装好W ...
- Spring框架——事务管理方式搭建一个小的项目
学习Spring框架,通过事务管理的方式搭建一个小的项目,该项目可以查询对数据库中的图书库存数量进行修改. 首先,使用MVC分层的设计模式思想搭建项目目录结构. 此部分代码源码之中都有相关注释,所以尽 ...
- 如何搭建一个WEB服务器项目(四)—— 实现安卓端图片加载
使用Glide安卓图片加载库 观前提示:本系列文章有关服务器以及后端程序这些概念,我写的全是自己的理解,并不一定正确,希望不要误人子弟.欢迎各位大佬来评论区提出问题或者是指出错误,分享宝贵经验.先谢谢 ...
随机推荐
- PostMan接口测试(很全面的接口测试教程)
一:理论部分 1. 前言 在前后端分离开发时,后端工作人员完成系统接口开发后,需要与前端人员对接,测试调试接口,验证接口的正确性可用性.而这要求前端开发进度和后端进度保持基本一致,任何一方的进度跟不上 ...
- 关于DNS解析:侧面剖析
作为一个合格的重度windows使用用户,我清楚的知道一个文件——hosts文件:C:\Windows\System32\drivers\etc\hosts文件 该文件需要一定的管理员权限. 这个文件 ...
- MySQL为某字段加前缀、后缀
在开发过程中,可能会遇到加前缀或者后缀的情况.比如为视频添加路径时,如果手动加起来肯定慢,而且比较不符合程序员的特点,我们就应该能让程序跑就不会手动加. 使用UPDATE sql 语句:update ...
- tp5--Excel表格导入导出
来源于:https://www.cnblogs.com/MyIsLu/p/6830579.html PHPExcel 扩展包下载地址: https://github.com/P ...
- koa+mysql实现增删改查-全栈之路(001)
Date: 2020-4-23 以前很少写文章,从今天开始我要挑战一下自己,连续输出100篇技术类文章.这100篇文章我尽量以实战案例为主. 如果你觉得本文还不错,记得关注或者给个 star,你们的赞 ...
- java学习(第三篇)String类
一.创建一个字符串 1.String(char a[]):用一个字符数组a创建一个string类型 char a[]= {'h','e','l','l','o'}; String s=new Stri ...
- Window+Protobuf使用说明
Window+Protobuf使用说明 C++WindowCmakeProtocbuf 介绍 起因 由于项目中要用到二进制存储数据,之前使用的方式是按照字节数依次将数据写入字节流中, 但是这样做起来做 ...
- css套路学习(一)
css3信息获取方法 文档搜索:Google css spec; Google: 关键词 MDN; css tricks成熟css代码块sinppets; Google: center css tri ...
- 题目分享G 二代目
题意:有n组数,每组包含两个数,问在每组只能取一个的前提下能组成的最长的从1开始的连续自然数有几个? 分析:刚学了差分约束系统,很容易往转换成图的方向去想 将他读入的这n组数当成边读入 很容易会得到一 ...
- MySQL 基础入门
MySQL 是最流行的关系型数据库管理系统,在 WEB 应用方面 MySQL 是最好的 RDBMS(Relational Database Management System:关系数据库管理系统)应用 ...