1. 阿里云OSS和百度人脸识别

1.1 需求分析

在登录判断的时候,我们需要根据用户的手机号查询用户是否为新用户,如果为新用户,那么在登录完成后需要跳转到完善用户信息界面,用户需要设置性别,头像和昵称等信息。具体的流程图如下:

客户端检测首次登录需要完善用户信息

  • 填写用户基本信息
  • 上传用户头像(需要人脸认证)

此外还需要考虑将用户头像保存的位置,目前主流的解决方案有三种:

  • 直接将图片保存到服务器的硬盘中

    • 优点:开发便捷,成本低
    • 缺点:扩容困难,且数据的安全性不能保证
  • 使用分布式的文件存储系统
    • 优点:方便扩容,且数据安全性高
    • 缺点:开发成本高
  • 第三方存储服务
    • 优点:开发简单,不需要维护,数据安全有保证
    • 缺点:付费

综合以上考虑,本项目中采用阿里云的第三方存储服务,具体的架构如下:

1.2 阿里云OSS的使用

阿里云OSS服务需要注册和购买套餐,这里就不在演示。直接来带在Java中如何使用。我们从官网上得到了实例代码,首先先在项目中建立一个测试类进行测试。

@Test
public void testOss() throws FileNotFoundException {//1、配置图片路径String path = "C:\\Users\\李宇轩\\Pictures\\unsplash--BZc9Ee1qo0.png";//2、构造FileInputStreamFileInputStream inputStream = new FileInputStream(new File(path));//3、拼写图片路径String filename = new SimpleDateFormat("yyyy/MM/dd").format(new Date())+"/"+ UUID.randomUUID().toString() + path.substring(path.lastIndexOf("."));// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。String endpoint = "oss-cn-qingdao.aliyuncs.com";// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。String accessKeyId = "dsadasdsadsacodc";String accessKeySecret = "8L3khmdsfewfw08s3ua3Wr8rMhjf";// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);// 填写Byte数组。// 填写Bucket名称和Object完整路径。Object完整路径中不能包含Bucket名称。ossClient.putObject("tanhuaossservice", filename, inputStream);// 关闭OSSClient。ossClient.shutdown();String url = "https://tanhuaossservice.oss-cn-qingdao.aliyuncs.com/" + filename;System.out.println(url);
}

阿里云文件上传后,需要我们拼接一个图片的URL地址,以后我们如果想要获取图像,那么只需要访问这个URL即可。

1.3 封装阿里云OSS服务的自动装配类

和之前发送短信验证码一样,我们需要将这个方法封装成一个可以自动装配的类,以后我们在其他工程中调用,只需要从IOC容器中即可获取。

创建OSSProperties

@Data
@ConfigurationProperties(prefix = "tanhua.oss")
public class OssProperties {private String endpoint;private String accessKeyId;private String accessKeySecret;private String bucket;private String url;
}

在配置文件中编写相关的配置信息

oss:endpoint: oss-cn-qingdao.aliyuncs.comaccessKeyId: LTAI5t8eyt6zxHZuE4wqewQTcodcaccessKeySecret: 8L3khm40RRbhZGn0dddww8s3ua3Wr8rMhjfbucket: tanhuddsadasaossserviceurl: https://tanhuaossservice.oss-cn-qingdao.aliyuncs.com/

创建OSSTemplate

public class OssTemplate {private OssProperties ossProperties;public OssTemplate(OssProperties ossProperties) {this.ossProperties = ossProperties;}public String uploadFile(String path, InputStream inputStream) {//3、拼写图片路径String filename = new SimpleDateFormat("yyyy/MM/dd").format(new Date())+ "/" + UUID.randomUUID().toString() + path.substring(path.lastIndexOf("."));// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。String endpoint = "oss-cn-qingdao.aliyuncs.com";// 阿里云主账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维,请登录 https://ram.console.aliyun.com 创建RAM账号。String accessKeyId = "LTAI5t8eyt6zxHZuE4QTcodc";String accessKeySecret = "8L3khm40RRbhZGn08s3ua3Wr8rMhjf";// 创建OSSClient实例。OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);// 填写Byte数组。// 填写Bucket名称和Object完整路径。Object完整路径中不能包含Bucket名称。ossClient.putObject("tanhuaossservice", filename, inputStream);// 关闭OSSClient。ossClient.shutdown();return "https://tanhuaossservice.oss-cn-qingdao.aliyuncs.com/" + filename;}
}

在自动装配类中注册OSSTemplate

@Beanpublic OssTemplate ossTemplate(OssProperties ossProperties) {return new OssTemplate(ossProperties);}

编写测试类测试

@Test
public void testTemplateUpload() throws IOException {String path = "C:\\Users\\李宇轩\\Desktop\\1.jpg";InputStream inputStream = Files.newInputStream(new File(path).toPath());String url = ossTemplate.uploadFile(path, inputStream);System.out.println(url);
}

1.4 百度人脸识别

探花交友APP定位社交场景,因此我们要求用户上传的头像必须是人像才可以,否则会提示用户头像非法。为了实现这个功能,我们借助百度的人脸识别API。

登录官网完成相关注册后,我们编写一个测试类来测试

@Test
public void testFace() {//设置APPID/AK/SKHashMap<String, String> options = new HashMap<String, String>();options.put("face_field", "age");options.put("max_face_num", "2");options.put("face_type", "LIVE");options.put("liveness_control", "LOW");// 初始化一个AipFaceAipFace client = new AipFace(APP_ID, API_KEY, SECRET_KEY);// 可选:设置网络连接参数client.setConnectionTimeoutInMillis(2000);client.setSocketTimeoutInMillis(60000);// 调用接口String image = "https://tanhuaossservice.oss-cn-qingdao.aliyuncs.com/2022/12/21/9dd44f73-1570-4706-afe3-20cb89d81aa6.png";String imageType = "URL";// 人脸检测JSONObject res = client.detect(image, imageType, options);System.out.println(res.toString(2));String error_code = res.get("error_code").toString();System.out.println(error_code);
}

1.5 封装人脸识别自动装配类

创建AipFaceProperties

@Data
@ConfigurationProperties(prefix = "tanhua.face")
public class AipFaceProperties {private String appId;private String apiKey;private String secretKey;}

编写相关配置

face:appId: 2932df58apiKey: ptSnMDA2321sbiMidBZ4wesecretKey: lAaU81AxY9BxrewrwP1ql4XdSGe2MF8ND0YK5

编写AipFaceTemplate

public boolean faceCheck(String imageUrl) {HashMap<String, String> options = new HashMap<String, String>();options.put("face_field", "age");options.put("max_face_num", "2");options.put("face_type", "LIVE");options.put("liveness_control", "LOW");// 初始化一个AipFaceAipFace client = new AipFace(aipFaceProperties.getAppId(), aipFaceProperties.getApiKey(), aipFaceProperties.getSecretKey());// 可选:设置网络连接参数client.setConnectionTimeoutInMillis(2000);client.setSocketTimeoutInMillis(60000);// 调用接口String image = "https://tanhuaossservice.oss-cn-qingdao.aliyuncs.com/2022/12/21/9dd44f73-1570-4706-afe3-20cb89d81aa6.png";String imageType = "URL";// 人脸检测JSONObject res = client.detect(image, imageType, options);return "0".equals(res.get("error_code").toString()) ? true : false;
}

编写测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class FaceTest {@Autowiredprivate AipFaceTemplate template;@Testpublic void detectFace() {String image = "https://tanhua001.oss-cn-beijing.aliyuncs.com/2021/04/19/a3824a45-70e3-4655-8106-a1e1be009a5e.jpg";boolean detect = template.detect(image);}
}

2. 完善用户信息

2.1 需求分析

2.1.1 流程分析


完善用户信息的流程

  • 首先用户需要选择性别和昵称以及出生年月。完成后会向UserInfo表中写入相关数据。
  • 接下来跳转到设置头像界面,用户从手机中选择一张图像,服务端接收到图像,保存到阿里云,并且调用百度人脸识别,判断头像是否合法。如果合法,则将头像的url保存到UserInfo表中的avatar字段中。

2.1.2 接口分析

用户完善信息的流程上面已经分析过了,这里就不在赘述。下面是完善用户信息的接口

2.1.3 数据库分析


  • 用户表和用户信息表是一对一的关系,两者采用主键关联的形式配置
  • 主键关联:用户表主键和用户资料表主键要保持一致(如:用户表id=1,此用户的资料表id=1)

2.2 代码实现

编写UserInfo类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo implements Serializable {/*** 由于userinfo表和user表之间是一对一关系* userInfo的id来源于user表的id*/@TableId(type = IdType.INPUT)private Long id; //用户idprivate String nickname; //昵称private String avatar; //用户头像private String birthday; //生日private String gender; //性别private Integer age; //年龄private String city; //城市private String income; //收入private String education; //学历private String profession; //行业private Integer marriage; //婚姻状态private String tags; //用户标签:多个用逗号分隔private String coverPic; // 封面图片private Date created;private Date updated;//用户状态,1为正常,2为冻结@TableField(exist = false)private String userStatus = "1";
}

编写Mapper UserInfoApi UserInfoApiImpl

package com.tanhua.dubbo.mappers;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.tanhua.model.domain.UserInfo;public interface UserInfoMapper extends BaseMapper<UserInfo> {}
public interface UserInfoApi {/*** 保存用户详细信息* @param userInfo*/void save(UserInfo userInfo);/*** 更新用户详细信息* @param userInfo*/void update(UserInfo userInfo);UserInfo getUserInfoById(Long userID);}
@DubboService
public class UserInfoApiImpl implements UserInfoApi {@Resourceprivate UserInfoMapper userInfoMapper;@Overridepublic void save(UserInfo userInfo) {this.userInfoMapper.insert(userInfo);}@Overridepublic void update(UserInfo userInfo) {this.userInfoMapper.updateById(userInfo);}@Overridepublic UserInfo getUserInfoById(Long userID) {return this.userInfoMapper.selectById(userID);}
}

编写Controller

/*** 新用户登录后完善个人信息* @param userInfo* @param token* @return*/
@PostMapping("/loginReginfo")
public ResponseEntity loginReginfo(@RequestBody UserInfo userInfo) {// 3. 将ID设置到userInfo中userInfo.setId(UserHolder.getUserId());// 4. 调用service方法保存数据this.userInfoService.save(userInfo);// 5. 返回结果return ResponseEntity.ok(null);
}/*** 完善个人信息之上传头像* @param headPhoto* @return*/
@PostMapping("/loginReginfo/head")
public ResponseEntity head(MultipartFile headPhoto) {// 3. 调用service将头像上传并进行人像判断this.userInfoService.uploadAvatar(UserHolder.getUserId(), headPhoto);// 4. 返回结果return ResponseEntity.ok(null);
}

编写Service中的方法

public void uploadAvatar(Long id, MultipartFile headPhoto) {String imageUrl = this.verifyFace(headPhoto);// 3. 更新用户信息UserInfo userInfo = new UserInfo();userInfo.setId(id);userInfo.setAvatar(imageUrl);this.userInfoApi.update(userInfo);
}public UserInfoVo getUserInfoById(Long userID) {UserInfo userInfo = this.userInfoApi.getUserInfoById(userID);UserInfoVo userInfoVo = new UserInfoVo();BeanUtils.copyProperties(userInfo, userInfoVo); // 只会拷贝名字相同且类型相同的属性userInfoVo.setAge(userInfo.getAge().toString());return userInfoVo;
}

3. 用户信息管理

3.1 查询用户详细资料

3.1.1 需求分析

用户在APP界面中点击我的,然后点击详细信息,可以看到自己账户的详细信息。

查询用户的接口如下

需要注意的是:如果请求中不带UserId参数,那么就标明用户查询的是当前自己的详细信息。如果携带参数,则查询指定id的用户详细信息。

3.1.2 代码实现

编写Controller

/*** 根据id查询用户的详细信息* @param userID* @param token* @return*/
@GetMapping
public ResponseEntity getUserInfoById(Long userID, @RequestHeader("Authorization") String token) {// 3. 判断ID是否为空if (userID == null) {// 查询当前用户的资料userID = UserHolder.getUserId();}// 4. 调用service方法查询用户信息UserInfoVo userInfoVo = this.userInfoService.getUserInfoById(userID);return ResponseEntity.ok(userInfoVo);
}

编写Service

public UserInfoVo getUserInfoById(Long userID) {UserInfo userInfo = this.userInfoApi.getUserInfoById(userID);UserInfoVo userInfoVo = new UserInfoVo();BeanUtils.copyProperties(userInfo, userInfoVo); // 只会拷贝名字相同且类型相同的属性userInfoVo.setAge(userInfo.getAge().toString());return userInfoVo;
}

注意 这里用到了VO,是因为前端接收的年龄信息是String类型的,而UserInfo中年龄是int类型,为了保证数据类型的一致性使用了Vo。

下面是一些常用的用来传递数据的实体类:
VO 值对象:通常用于服务端和界面之间的数据传递。VO对象通常是后端给前端传递数据
DTO对象:数据传输对象。DTO对象通常是用来封装前端传递的参数给后端
Entity:我们最常见的实体类。通常情况下一个表对应一个实体类

在UserInfoApi中添加根据ID查询用户详细信息方法

@Override
public UserInfo getUserInfoById(Long userID) {return this.userInfoMapper.selectById(userID);
}

3.2 更新用户详细资料

3.2.1 需求分析

用户可以在自己的用户详细信息界面更新自己的个人资料。其接口如下。

3.2.2 代码实现

在Controller中添加一个方法用来更新

/*** 用户在我的界面中修改自己的个人信息* @param userInfo* @param token* @return*/
@PutMapping
public ResponseEntity updateUserInfo(@RequestBody UserInfo userInfo, @RequestHeader("Authorization") String token) {// 3. 设置userIduserInfo.setId(UserHolder.getUserId());// 4. 调用service方法 更新用户信息this.userInfoService.updateUserInfo(userInfo);// 5. 返回响应信息return ResponseEntity.ok(null);
}

在Service中编写更新用户信息的方法

public void updateUserInfo(UserInfo userInfo) {this.userInfoApi.update(userInfo);
}

userInfoApi中的update方法之前在编写上传用户头像的时候就已经完成

3.3 更新头像

3.3.1 需求分析

用户可以在个人信息页面修改自己的头像。修改头像的流程和新用户注册上传头像一致。
接口如下:

3.3.2 代码实现

在Controller中添加更新头像的方法

/*** 更新头像* @param headPhoto* @return*/
@PostMapping("/header")
public ResponseEntity updateHeader(MultipartFile headPhoto) {this.userInfoService.updateHeader(headPhoto);return ResponseEntity.ok(null);
}

在Service中添加更新头像方法

/*** 更新用户头像** @param headPhoto*/
public void updateHeader(MultipartFile headPhoto) {// 1. 验证头像是否合法String imageUrl = this.verifyFace(headPhoto);// 2. 将信息保存到UserInfo表中UserInfo userInfo = new UserInfo();userInfo.setId(UserHolder.getUserId());userInfo.setAvatar(imageUrl);this.userInfoApi.update(userInfo);
}

由于在上传头像和更新头像中都需要验证头像是否合法,因此抽取成一个独立的方法

/*** 验证人脸是否合法** @param headPhoto* @return*/
private String verifyFace(MultipartFile headPhoto) {// 1. 将文件上传到阿里云OSSString imageUrl = null;try {imageUrl = ossTemplate.uploadFile(headPhoto.getOriginalFilename(), headPhoto.getInputStream());} catch (IOException e) {throw new BusinessException(ErrorResult.error());}// 2. 调用百度人脸识别判断是否是人脸,如果不是抛出异常boolean check = aipFaceTemplate.faceCheck(imageUrl);if (false) {throw new BusinessException(ErrorResult.faceError());}return imageUrl;
}

4. 代码优化

4.1 统一用户登陆状态验证

4.1.1 存在的问题

在之前的代码中,我们每次都需要从Controller中获取到用户登录的Token,然后验证Token是否合法,如果不合法则返回异常;如果合法则从token中解析中用户的信息。每一个Controller都需要这样做导致代码冗余,因此我们需要借助拦截器的方式来对我们的代码进行优化。

我们的解决方案是

  • 使用SpringMVC中的拦截器来对所有请求进行拦截,在拦截器中判断用户的登录信息
  • 我们在拦截器中对用户的token进行解析,并将解析到的信息存储到ThreadLocal

4.1.2 代码实现

创建UserHolder用来保存登录用户的信息

public class UserHolder {private static ThreadLocal<User> threadLocal = new ThreadLocal<>();/*** 将用户保存到threadLocal* @param user*/public static void setUser(User user) {threadLocal.set(user);}/*** 从threadLocal获取用户* @return*/public static User getUser() {return threadLocal.get();}public static Long getUserId() {return threadLocal.get().getId();}public static String getUserPhone() {return threadLocal.get().getMobile();}/*** 请求执行完毕后将用户信息用threadLocal中删除*/public static void remove() {threadLocal.remove();}}

创建TokenInterceptor,完成用户登录信息验证和token解析

public class TokenInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 1. 获取tokenString token = request.getHeader("Authorization");// 2. 判断token是否合法 如果不合法 返回401错误boolean flag = JwtUtils.verifyToken(token);if (!flag) {// token不合法response.setStatus(401);return false;}// 3. 解析token,将用户信息保存到threadLocalClaims claims = JwtUtils.getClaims(token);Integer id = (Integer) claims.get("id");String mobile = claims.get("mobile").toString();User user = new User();user.setId(Long.valueOf(id));user.setMobile(mobile);UserHolder.setUser(user);// 4. 放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.remove();}
}

需要注意的是,随着登录用户越来越多,ThreadLocal也会占用大量的内存空间,因此最好当一个请求执行完毕后,就释放掉ThreadLocal中的内容。如afterCompletion方法中所示

编写SpringMVC配置类,配置拦截器的相关属性

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new TokenInterceptor()).addPathPatterns("/**").excludePathPatterns("/user/login", "/user/loginVerification");}
}

4.2 统一异常处理

4.2.1 存在的问题

在开发过程中,不可避免的要处理很多的异常,常见的异常处理形式是向上抛出,在Controller层进行处理。但是这样的处理方式导致异常处理代码和业务逻辑代码混在一起,降低代码的可读性。


为了解决这个问题,我们利用SpringMVC中的统一异常处理机制

所有的异常都进行抛出,SpringMVC会在核心控制器中进行捕获,并调用我们编写的异常处理器进行处理。

4.2.2 代码实现

编写一个类将一些常见的业务错误封装到ErrorResult对象中

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ErrorResult {private String errCode = "999999";private String errMessage;public static ErrorResult error() {return ErrorResult.builder().errCode("999999").errMessage("系统异常稍后再试").build();}public static ErrorResult fail() {return ErrorResult.builder().errCode("000001").errMessage("发送验证码失败").build();}public static ErrorResult loginError() {return ErrorResult.builder().errCode("000002").errMessage("验证码失效").build();}public static ErrorResult faceError() {return ErrorResult.builder().errCode("000003").errMessage("图片非人像,请重新上传!").build();}public static ErrorResult mobileError() {return ErrorResult.builder().errCode("000004").errMessage("手机号码已注册").build();}public static ErrorResult contentError() {return ErrorResult.builder().errCode("000005").errMessage("动态内容为空").build();}public static ErrorResult likeError() {return ErrorResult.builder().errCode("000006").errMessage("用户已点赞").build();}public static ErrorResult disLikeError() {return ErrorResult.builder().errCode("000007").errMessage("用户未点赞").build();}public static ErrorResult loveError() {return ErrorResult.builder().errCode("000008").errMessage("用户已喜欢").build();}public static ErrorResult disloveError() {return ErrorResult.builder().errCode("000009").errMessage("用户未喜欢").build();}
}

编写我们自定义的业务异常

/*** 自定义异常类*/
@Data
public class BusinessException extends RuntimeException {private ErrorResult errorResult;public BusinessException(ErrorResult errorResult) {super(errorResult.getErrMessage());this.errorResult = errorResult;}
}

编写全局异常处理类

@ControllerAdvice
public class GlobalExceptionHandler {/*** 处理业务逻辑异常* @param businessException* @return*/@ExceptionHandler(BusinessException.class)public ResponseEntity handleBusinessException(BusinessException businessException) {businessException.printStackTrace();ErrorResult result = businessException.getResult();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result);}@ExceptionHandler(Exception.class)public ResponseEntity handleOtherExceptions(Exception e) {e.printStackTrace();return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ErrorResult.error());}}

修改原来的抛出异常的代码

if (StringUtils.isEmpty(redisCode) || !redisCode.equals(code)) {// 验证码无效或者验证码错误throw new BusinessException(ErrorResult.loginError());
}
if (false) {throw new BusinessException(ErrorResult.faceError());
}

【探花交友DAY 03】个人信息完善 阿里云OSS百度人脸识别引入 统一Token和异常处理相关推荐

  1. 阿里云平台的人脸识别接口测试

    需要导入的包: 必备模块:import urllib 请求模块:import urllib.request 解析模块:import urllib.parse  可以拆分URL,也可以拼接URL 数据交 ...

  2. 关于图片阿里云OSS的处理x-oss-process

    可能有的时候会发现发现有的代码图片连接会是以下的形式: https://xxxxx.xxx.xxx.com/images/xxxxx.png?x-oss-process=style/XXXX 其实有的 ...

  3. 探花交友_第2章-完善个人信息与MongoDB入门

    探花交友_第2章-完善个人信息与MongoDB入门 文章目录 探花交友_第2章-完善个人信息与MongoDB入门 1.完善个人信息 1.1.图片上传 1.1.1.图片存储解决方案 1.1.2.阿里云O ...

  4. 探花交友_第7章-完善消息功能以及个人主页

    探花交友_第7章-完善消息功能以及个人主页 文章目录 探花交友_第7章-完善消息功能以及个人主页 1.消息点赞.喜欢.评论列表 1.1.dubbo服务 1.1.1.定义接口 1.1.2.编写实现 1. ...

  5. 【探花交友】阿里云OSS、百度人脸识别

    文章目录 1.完善用户信息 1.1.阿里云OSS 1.2.百度人脸识别 1.完善用户信息 用户在首次登录时需要完善个人信息,包括性别.昵称.生日.城市.头像等.其中,头像数据需要做图片上传,这里采用阿 ...

  6. 【探花交友】保存用户信息、上传用户头像、用户信息管理

    文章目录 1.3.保存用户信息 1.4.上传用户头像 2.用户信息管理 2.1.查询用户资料 2.2.更新用户资料 1.3.保存用户信息 1.3.1.接口文档 YAPI接口地址:http://192. ...

  7. 在线教育_Day06_项目整合阿里云OSS和Excel导入分类

    一.阿里云OSS概述及开通 1.1 对象存储OSS 为了解决海量数据存储与弹性扩容,项目中我们采用云存储的解决方案- 阿里云OSS. 1.2 开通"对象存储OSS"服务 (1)申请 ...

  8. 微信小程序直接上传文件到阿里云OSS组件封装

    微信小程序直接上传文件到OSS 1. 封装公共方法 在根目录utils目录新建一个upload文件夹: // utils/upload/base64.jsvar base64EncodeChars = ...

  9. JAVA通过阿里云OSS存储实现图片上传功能

    一.前置准备 首先我们需要在阿里云注册账号,实名认证后开通OSS功能,点击进入OSS功能的管理平台 进入概览页面后,点击Bucket列表,创建一个Bucket(相当于一个存放文件的文件夹)  关键是要 ...

最新文章

  1. FileChannel应用实例——本地文件读数据
  2. 文巾解题 面试题 01.02. 判定是否互为字符重排
  3. 7.18 collection random os sys等模块
  4. 基于visual Studio2013解决面试题之1305字符串所有子集
  5. 全网首发:JDK绘制文字:一、绘制流程
  6. TCP长连接开发相关,调试工具SocketTool与框架GatewayWorker
  7. HTML5+CSS3从入门到精通随书光盘 ISO 镜像视频教程​
  8. NAT技术及NAT ALG
  9. java intern_Java intern() 方法
  10. ijkplayer 代码走读之 播放器网络数据读取过程详解2
  11. 扩展名为ifo的文件怎么播放
  12. 计算机技术与软件专业技术资格考试(初级程序员)(一)
  13. 闲暇时间不知道如何有效利用,TapTap、触漫等五款软件帮你解决
  14. BugkuCTF 部分题解(随缘更新)
  15. Orcal ETL 增量抽取--ORA_ROWSCN伪列
  16. 创建型模式:工厂方法
  17. 计算机系迎新晚会策划,迎新晚会策划方案
  18. 一年级关于计算机的手抄报内容是什么,一年级春天来了手抄报内容资料
  19. ArcMap中CGCS2000与WGS80坐标系转换
  20. 2022-1-15 Leetcode 165.比较版本号

热门文章

  1. 根据文件流下载表格数据Excel形式
  2. Loopback 录屏和录音频(Mac录屏教程/录视频教程/Loopback 教程)
  3. vector不是模板 list不是模板
  4. #2.3数理比较与分析
  5. JSoup模拟登录网站(以校园内网为例)
  6. centios修改盘符
  7. h5调用摄像头+拍照+上传图片-----h5+js+ajax
  8. 高德地图引入Vue添加POI搜索功能、marker点标记、通过经纬度逆编码过程
  9. Android手机中获取手机号码和运营商信息
  10. 根据身份证地址拆分省市区