新增附件上传接口

master
bmpandlcm 1 year ago
parent 55bd770c31
commit 592c85184d

@ -9,13 +9,14 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="29bcb86b-1634-41e0-a498-79481163cba8" name="Changes" comment="人员认证加了一个校验"> <list default="true" id="29bcb86b-1634-41e0-a498-79481163cba8" name="Changes" comment="给后端服务提供的websocket进行消息内容修改">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/config/SubscribeListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/config/SubscribeListener.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/pom.xml" beforeDir="false" afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/UserController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/UserController.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/FileController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/FileController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/UserVehicleController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/UserVehicleController.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/UserVehicleController.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/UserVehicleController.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/WebSocketRedis.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/controller/WebSocketRedis.java" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/service/impl/UserTableServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/service/impl/UserTableServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/resources/static/Sensitive.txt" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/resources/static/Sensitive.txt" afterDir="false" /> <change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/service/impl/UserVehicleServiceImpl.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/service/impl/UserVehicleServiceImpl.java" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/main/java/com/nmgs/util/ImageUtils.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/java/com/nmgs/util/ImageUtils.java" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -72,58 +73,58 @@
<component name="ProjectViewState"> <component name="ProjectViewState">
<option name="showLibraryContents" value="true" /> <option name="showLibraryContents" value="true" />
</component> </component>
<component name="PropertiesComponent">{ <component name="PropertiesComponent"><![CDATA[{
&quot;keyToString&quot;: { "keyToString": {
&quot;Application.DivPassDataImpl.executor&quot;: &quot;Run&quot;, "Application.DivPassDataImpl.executor": "Run",
&quot;Application.ImageUtils.executor&quot;: &quot;Run&quot;, "Application.ImageUtils.executor": "Run",
&quot;Application.PubTools.executor&quot;: &quot;Run&quot;, "Application.PubTools.executor": "Run",
&quot;Application.QCodeInfoServiceImpl.executor&quot;: &quot;Debug&quot;, "Application.QCodeInfoServiceImpl.executor": "Debug",
&quot;Application.SensitiveFilter.executor&quot;: &quot;Run&quot;, "Application.SensitiveFilter.executor": "Run",
&quot;Application.main.executor&quot;: &quot;Debug&quot;, "Application.main.executor": "Debug",
&quot;Application.redisConfigUtil.executor&quot;: &quot;Debug&quot;, "Application.redisConfigUtil.executor": "Debug",
&quot;Maven.IntegraManager [clean].executor&quot;: &quot;Run&quot;, "Maven.IntegraManager [clean].executor": "Run",
&quot;Maven.IntegraManager [install].executor&quot;: &quot;Run&quot;, "Maven.IntegraManager [install].executor": "Run",
&quot;Maven.IntegralManager [clean].executor&quot;: &quot;Run&quot;, "Maven.IntegralManager [clean].executor": "Run",
&quot;Maven.IntegralManager [install].executor&quot;: &quot;Run&quot;, "Maven.IntegralManager [install].executor": "Run",
&quot;Maven.IntegralManager [validate].executor&quot;: &quot;Run&quot;, "Maven.IntegralManager [validate].executor": "Run",
&quot;Maven.IntegralManagerSys [clean].executor&quot;: &quot;Run&quot;, "Maven.IntegralManagerSys [clean].executor": "Run",
&quot;Maven.IntegralManagerSys [install].executor&quot;: &quot;Run&quot;, "Maven.IntegralManagerSys [install].executor": "Run",
&quot;Maven.devProject [clean].executor&quot;: &quot;Run&quot;, "Maven.devProject [clean].executor": "Run",
&quot;Maven.devProject [install].executor&quot;: &quot;Run&quot;, "Maven.devProject [install].executor": "Run",
&quot;Maven.dev_project [clean].executor&quot;: &quot;Run&quot;, "Maven.dev_project [clean].executor": "Run",
&quot;Maven.dev_project [install].executor&quot;: &quot;Run&quot;, "Maven.dev_project [install].executor": "Run",
&quot;Maven.special_event [clean].executor&quot;: &quot;Run&quot;, "Maven.special_event [clean].executor": "Run",
&quot;Maven.special_event [install].executor&quot;: &quot;Run&quot;, "Maven.special_event [install].executor": "Run",
&quot;Maven.special_event [org.apache.maven.plugins:maven-assembly-plugin:3.3.0:single].executor&quot;: &quot;Run&quot;, "Maven.special_event [org.apache.maven.plugins:maven-assembly-plugin:3.3.0:single].executor": "Run",
&quot;Maven.special_event [org.springframework.boot:spring-boot-maven-plugin:2.7.11:repackage].executor&quot;: &quot;Run&quot;, "Maven.special_event [org.springframework.boot:spring-boot-maven-plugin:2.7.11:repackage].executor": "Run",
&quot;Maven.special_event [package].executor&quot;: &quot;Run&quot;, "Maven.special_event [package].executor": "Run",
&quot;Maven.special_event [validate].executor&quot;: &quot;Run&quot;, "Maven.special_event [validate].executor": "Run",
&quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;, "RunOnceActivity.OpenProjectViewOnStart": "true",
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, "RunOnceActivity.ShowReadmeOnStart": "true",
&quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;, "SHARE_PROJECT_CONFIGURATION_FILES": "true",
&quot;TomEE Server.special_event.executor&quot;: &quot;Debug&quot;, "TomEE Server.special_event.executor": "Debug",
&quot;Tomcat Server.Tomcat 9.0.80.executor&quot;: &quot;Debug&quot;, "Tomcat Server.Tomcat 9.0.80.executor": "Debug",
&quot;deletionFromPopupRequiresConfirmation&quot;: &quot;false&quot;, "deletionFromPopupRequiresConfirmation": "false",
&quot;git-widget-placeholder&quot;: &quot;master&quot;, "git-widget-placeholder": "master",
&quot;kotlin-language-version-configured&quot;: &quot;true&quot;, "kotlin-language-version-configured": "true",
&quot;last_opened_file_path&quot;: &quot;E:/work/TGGLT-WorkSpace/IntegralManager&quot;, "last_opened_file_path": "E:/work/TGGLT-WorkSpace/special_event",
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;, "node.js.detected.package.eslint": "true",
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;, "node.js.detected.package.tslint": "true",
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;, "node.js.selected.package.eslint": "(autodetect)",
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;, "node.js.selected.package.tslint": "(autodetect)",
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;, "nodejs_package_manager_path": "npm",
&quot;project.structure.last.edited&quot;: &quot;Modules&quot;, "project.structure.last.edited": "Modules",
&quot;project.structure.proportion&quot;: &quot;0.15&quot;, "project.structure.proportion": "0.15",
&quot;project.structure.side.proportion&quot;: &quot;0.37169158&quot;, "project.structure.side.proportion": "0.37169158",
&quot;settings.editor.selected.configurable&quot;: &quot;vcs.Git&quot;, "settings.editor.selected.configurable": "vcs.Git",
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot; "vue.rearranger.settings.migration": "true"
}, },
&quot;keyToStringList&quot;: { "keyToStringList": {
&quot;DatabaseDriversLRU&quot;: [ "DatabaseDriversLRU": [
&quot;oracle&quot; "oracle"
] ]
} }
}</component> }]]></component>
<component name="ReactorSettings"> <component name="ReactorSettings">
<option name="notificationShown" value="true" /> <option name="notificationShown" value="true" />
</component> </component>
@ -529,6 +530,10 @@
<workItem from="1730798249931" duration="969000" /> <workItem from="1730798249931" duration="969000" />
<workItem from="1730799271800" duration="752000" /> <workItem from="1730799271800" duration="752000" />
<workItem from="1730875183642" duration="901000" /> <workItem from="1730875183642" duration="901000" />
<workItem from="1731025124463" duration="72000" />
<workItem from="1731049718437" duration="3388000" />
<workItem from="1731392801577" duration="11435000" />
<workItem from="1731462347759" duration="16550000" />
</task> </task>
<task id="LOCAL-00001" summary="特请处理"> <task id="LOCAL-00001" summary="特请处理">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -618,7 +623,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1729842478482</updated> <updated>1729842478482</updated>
</task> </task>
<option name="localTasksCounter" value="12" /> <task id="LOCAL-00012" summary="给后端服务提供的websocket进行消息内容修改">
<option name="closed" value="true" />
<created>1731025181036</created>
<option name="number" value="00012" />
<option name="presentableId" value="LOCAL-00012" />
<option name="project" value="LOCAL" />
<updated>1731025181036</updated>
</task>
<option name="localTasksCounter" value="13" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@ -634,7 +647,8 @@
<MESSAGE value="修改附件查询地址" /> <MESSAGE value="修改附件查询地址" />
<MESSAGE value="修改redis 配置以及websocket信息" /> <MESSAGE value="修改redis 配置以及websocket信息" />
<MESSAGE value="人员认证加了一个校验" /> <MESSAGE value="人员认证加了一个校验" />
<option name="LAST_COMMIT_MESSAGE" value="人员认证加了一个校验" /> <MESSAGE value="给后端服务提供的websocket进行消息内容修改" />
<option name="LAST_COMMIT_MESSAGE" value="给后端服务提供的websocket进行消息内容修改" />
</component> </component>
<component name="XDebuggerManager"> <component name="XDebuggerManager">
<breakpoint-manager> <breakpoint-manager>

@ -213,7 +213,11 @@
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<version>4.5.14</version> <version>4.5.14</version>
</dependency> </dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies> </dependencies>

@ -6,20 +6,24 @@ import com.nmgs.config.ResultData;
import com.nmgs.util.*; import com.nmgs.util.*;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.commons.io.FileUtils;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File; import java.io.File;
import java.util.Base64; import java.util.Base64;
import java.util.Properties; import java.util.Properties;
@RestController @Controller
@RequestMapping(value = "/fileSolve") @RequestMapping(value = "/fileSolve")
@Api(tags = "附件处理") @Api(tags = "附件处理")
@Async("integralManagerHttpThreadPool") @Async("integralManagerHttpThreadPool")
public class FileController { public class FileController {
@RequestMapping(value = "/delFile", method = {RequestMethod.POST}) @RequestMapping(value = "/delFile", method = {RequestMethod.POST})
@ApiOperation(value = "删除附件") @ApiOperation(value = "删除附件")
@ResponseBody
public ResultData<String> delFile(@RequestParam(value = "fileName") String fileName, @RequestParam(value = "canDel") String canDel) { public ResultData<String> delFile(@RequestParam(value = "fileName") String fileName, @RequestParam(value = "canDel") String canDel) {
boolean isXunhuan = false; boolean isXunhuan = false;
if ("Y".equals(canDel)) { if ("Y".equals(canDel)) {
@ -33,6 +37,7 @@ public class FileController {
@RequestMapping(value = "/uploadFile", method = {RequestMethod.POST}) @RequestMapping(value = "/uploadFile", method = {RequestMethod.POST})
@ApiOperation(value = "上传附件到本项目和远程指定目录") @ApiOperation(value = "上传附件到本项目和远程指定目录")
@ResponseBody
public ResultData<String> uploadFile(@RequestParam("jsonArray") String jsonArray) { public ResultData<String> uploadFile(@RequestParam("jsonArray") String jsonArray) {
try { try {
LogUtil.WriteLog_Info("附件上传接口执行开始===========" + PubTools.getCurrentDate(), "FileController"); LogUtil.WriteLog_Info("附件上传接口执行开始===========" + PubTools.getCurrentDate(), "FileController");
@ -84,4 +89,6 @@ public class FileController {
} }
return ResultData.success(1, "上传成功"); return ResultData.success(1, "上传成功");
} }
} }

@ -135,9 +135,9 @@ public class UserVehicleController {
@RequestParam("ACARNO") String ACARNO, @RequestParam("ACARNO") String ACARNO,
@RequestParam(value = "CHECKUSERNAME",required = false) String CHECKUSERNAME, @RequestParam(value = "CHECKUSERNAME",required = false) String CHECKUSERNAME,
@RequestParam(value ="CHECKUSERNUMBER",required = false) String CHECKUSERNUMBER, @RequestParam(value ="CHECKUSERNUMBER",required = false) String CHECKUSERNUMBER,
@RequestParam(value ="LICENSEPIC",required = false) String LICENSEPIC, @RequestParam(value = "LICENSEPIC", required = true) String LICENSEPIC,
@RequestParam(value ="PERMITPIC",required = false) String PERMITPIC, @RequestParam(value = "PERMITPIC", required = true) String PERMITPIC,
@RequestParam(value ="VEHICLEPIC",required = false) String VEHICLEPIC, @RequestParam(value = "VEHICLEPIC", required = true) String VEHICLEPIC,
@RequestParam(value ="CERTIFICATION",required = false) String CERTIFICATION, @RequestParam(value ="CERTIFICATION",required = false) String CERTIFICATION,
@RequestParam(value ="REFERRERCODE",required = false) String REFERRERCODE @RequestParam(value ="REFERRERCODE",required = false) String REFERRERCODE
){ ){

@ -124,7 +124,7 @@ public class UserTableServiceImpl implements UserTableService {
//图片加水印 //图片加水印
String fileName = userId + "NUMPIC" + ".jpg"; String fileName = userId + "NUMPIC" + ".jpg";
LogUtil.WriteLog_Info("用户实名认证修改附件水印=开始===" + PubTools.getCurrentDate() + "====", "UserController"); LogUtil.WriteLog_Info("用户实名认证修改附件水印=开始===" + PubTools.getCurrentDate() + "====", "UserController");
if(!PubTools.isNull(numberPic)){ if (!PubTools.isNull(numberPic) && !numberPic.contains("NUMPIC")) {
LogUtil.WriteLog_Info("用户实名认证修改附件水印===IF里面=" + PubTools.getCurrentDate() + "====", "UserController"); LogUtil.WriteLog_Info("用户实名认证修改附件水印===IF里面=" + PubTools.getCurrentDate() + "====", "UserController");
String permitpicPic = ImageUtils.addImageWaterMark(numberPic, "仅用于内蒙古交通货运车辆积分会员认证", fileName); String permitpicPic = ImageUtils.addImageWaterMark(numberPic, "仅用于内蒙古交通货运车辆积分会员认证", fileName);
LogUtil.WriteLog_Info("用户实名认证修改附件水印=完成===" + PubTools.getCurrentDate() + "====", "UserController"); LogUtil.WriteLog_Info("用户实名认证修改附件水印=完成===" + PubTools.getCurrentDate() + "====", "UserController");
@ -132,7 +132,6 @@ public class UserTableServiceImpl implements UserTableService {
numberPic=permitpicPic; numberPic=permitpicPic;
} }
} }
if("2".equals(DBType)){ if("2".equals(DBType)){
numberPic=PubTools.StringToClob(numberPic); numberPic=PubTools.StringToClob(numberPic);
} }

@ -268,21 +268,21 @@ public class UserVehicleServiceImpl implements UserVehicleService {
String fileNamePre = userId + "-" + aCarNo + "-"; String fileNamePre = userId + "-" + aCarNo + "-";
String fileNameAfter = ".jpg"; String fileNameAfter = ".jpg";
//图片加水印 //图片加水印
if(!PubTools.isNull(permitpic)){ if (!PubTools.isNull(permitpic) && !permitpic.contains("PERPIC")) {
String permitpicPic = ImageUtils.addImageWaterMark(permitpic, "仅用于内蒙古交通货运车辆积分会员认证", fileNamePre + "PERPIC" + fileNameAfter); String permitpicPic = ImageUtils.addImageWaterMark(permitpic, "仅用于内蒙古交通货运车辆积分会员认证", fileNamePre + "PERPIC" + fileNameAfter);
if(!PubTools.isNull(permitpicPic)){ if(!PubTools.isNull(permitpicPic)){
permitpic=permitpicPic; permitpic=permitpicPic;
} }
} }
//图片加水印 //图片加水印
if(!PubTools.isNull(licensepic)){ if (!PubTools.isNull(licensepic) && !licensepic.contains("LICPIC")) {
String permitpicPic = ImageUtils.addImageWaterMark(licensepic, "仅用于内蒙古交通货运车辆积分会员认证", fileNamePre + "LICPIC" + fileNameAfter); String permitpicPic = ImageUtils.addImageWaterMark(licensepic, "仅用于内蒙古交通货运车辆积分会员认证", fileNamePre + "LICPIC" + fileNameAfter);
if(!PubTools.isNull(permitpicPic)){ if(!PubTools.isNull(permitpicPic)){
licensepic=permitpicPic; licensepic=permitpicPic;
} }
} }
//图片加水印 //图片加水印
if(!PubTools.isNull(vehiclePic)){ if (!PubTools.isNull(vehiclePic) && !vehiclePic.contains("VEHPIC")) {
String permitpicPic = ImageUtils.addImageWaterMark(vehiclePic, "仅用于内蒙古交通货运车辆积分会员认证", fileNamePre + "VEHPIC" + fileNameAfter); String permitpicPic = ImageUtils.addImageWaterMark(vehiclePic, "仅用于内蒙古交通货运车辆积分会员认证", fileNamePre + "VEHPIC" + fileNameAfter);
if(!PubTools.isNull(permitpicPic)){ if(!PubTools.isNull(permitpicPic)){
vehiclePic=permitpicPic; vehiclePic=permitpicPic;
@ -340,25 +340,7 @@ public class UserVehicleServiceImpl implements UserVehicleService {
}catch (Exception e){ }catch (Exception e){
LogUtil.WriteLog_Error("批量修改车牌失败===>"+e.getMessage(),"UserVehicleServiceImpl"); LogUtil.WriteLog_Error("批量修改车牌失败===>"+e.getMessage(),"UserVehicleServiceImpl");
return ret=-1; return ret=-1;
//throw new RuntimeException();
} }
return ret; return ret;
} }
/* public String ClobToString(Object clob) throws Exception {
String reString = "";
try {
if(clob instanceof CLOB){
reString = ((CLOB)clob).getSubString((long) 1, (int) ((CLOB)clob).length());
}else if(clob instanceof Blob){
reString = clob.toString();
}
} catch (Exception e) {
LogUtil.WriteLog_Error("Oracle数据由Clob类型转化String类型处理失败====>"+ e.getMessage(),"UserTableServiceImpl");
throw new Exception("Oracle数据处理失败");
}
return reString;
}*/
} }

@ -12,6 +12,9 @@ import javax.imageio.ImageIO;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;
@Async @Async
public class ImageUtils { public class ImageUtils {
@ -36,6 +39,7 @@ public class ImageUtils {
} else { } else {
// fileAddress = PropertiesUtil.getValue("fileAddressLinux"); // fileAddress = PropertiesUtil.getValue("fileAddressLinux");
} }
imageBase64 = imageBase64.replaceAll(" ", "+").replace("\r\n", "");
byte[] imageBytes = Base64.getDecoder().decode(imageBase64); byte[] imageBytes = Base64.getDecoder().decode(imageBase64);
// 构造 BufferedImage 对象 // 构造 BufferedImage 对象
inputStream = new ByteArrayInputStream(imageBytes); inputStream = new ByteArrayInputStream(imageBytes);
@ -211,4 +215,46 @@ public class ImageUtils {
} }
return 1; return 1;
} }
public static String byteToBase64(byte[] b) {
try {
BASE64Encoder encoder = new BASE64Encoder();
return encoder.encode(b);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* imgFile
*/
public static String getImgFileToBase64(String imgFile) {
//将图片文件转化为字节数组字符串并对其进行Base64编码处理
InputStream inputStream = null;
byte[] buffer = null;
//读取图片字节数组
try {
inputStream = new FileInputStream(imgFile);
int count = 0;
while (count == 0) {
count = inputStream.available();
}
buffer = new byte[count];
inputStream.read(buffer);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
// 关闭inputStream流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 对字节数组Base64编码
return new BASE64Encoder().encode(buffer);
}
} }
Loading…
Cancel
Save