diff --git a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java index 2f04adfb2..f42471769 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java @@ -399,14 +399,18 @@ public class ConfigController { @RequestParam(value = "ids", required = false)List ids) { try { // 暂时不考虑数据量大的问题,配制应该不会很多 + StringBuilder idsSb = new StringBuilder(); - for(int i = 0; i < ids.size(); i++){ - Long id = ids.get(i); - idsSb.append(id); - if((i + 1) < ids.size()){ - idsSb.append(","); + if(ids != null && !ids.isEmpty()){ + for(int i = 0; i < ids.size(); i++){ + Long id = ids.get(i); + idsSb.append(id); + if((i + 1) < ids.size()){ + idsSb.append(","); + } } } + List dataList = persistService.findAllConfigInfo4eExport(group, tenant, appName, idsSb.toString()); List zipItemList = new ArrayList<>(); StringBuilder metaData = null; @@ -419,12 +423,15 @@ public class ConfigController { String metaDataId = ci.getDataId().substring(0,ci.getDataId().lastIndexOf(".")) + "~" + ci.getDataId().substring(ci.getDataId().lastIndexOf(".") + 1); metaData.append(ci.getGroup()).append(".").append(metaDataId).append(".app=") - .append(ci.getAppName()).append("\r\n"); + .append(ci.getAppName()).append("\r\n"); // ACM使用的是 \r\n, 不是根据系统取的 } String itemName = ci.getGroup() + "/" + ci.getDataId() ; zipItemList.add(new ZipUtils.ZipItem(itemName, ci.getContent())); } - zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaData.toString())); + if(metaData != null){ + zipItemList.add(new ZipUtils.ZipItem(".meta.yml", metaData.toString())); + } + HttpHeaders headers = new HttpHeaders(); String fileName="nacos_config_export_" + DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss") + ".zip"; headers.add("Content-Disposition", "attachment;filename="+fileName); @@ -436,43 +443,84 @@ public class ConfigController { } } -// @RequestMapping(params = "import=true", method = RequestMethod.POST) -// @ResponseBody -// public RestResult importAndPublishConfig(HttpServletRequest request, HttpServletResponse response, -// @RequestParam(value = "src_user", required = false) String srcUser, -// MultipartFile file) throws NacosException { -// RestResult rr = new RestResult(); -// List configInfoList = null; -// try { -// configInfoList = (List) JSONUtils.deserializeObject( ZipUtils.unzip(file.getBytes()), new TypeReference>() { -// }); -// } catch (IOException e) { -// rr.setCode(500); -// rr.setData(false); -// rr.setMessage("parsing data failed"); -// log.error("parsing data failed", e); -// return rr; -// } -// if (configInfoList == null || configInfoList.isEmpty()) { -// rr.setCode(500); -// rr.setData(false); -// rr.setMessage("data is empty"); -// return rr; -// } -// final String srcIp = RequestUtil.getRemoteIp(request); -// String requestIpApp = RequestUtil.getAppName(request); -// final Timestamp time = TimeUtils.getCurrentTime(); -// persistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, null, time, false); -// for (ConfigInfo configInfo : configInfoList) { -// EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(), -// configInfo.getTenant(), time.getTime())); -// ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), -// configInfo.getTenant(), requestIpApp, time.getTime(), -// LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent()); -// } -// rr.setCode(200); -// rr.setData(true); -// rr.setMessage("import ok"); -// return rr; -// } + @RequestMapping(params = "import=true", method = RequestMethod.POST) + @ResponseBody + public RestResult importAndPublishConfig(HttpServletRequest request, HttpServletResponse response, + @RequestParam(value = "src_user", required = false) String srcUser, + @RequestParam(value = "namespace", required = false) String namespace, + MultipartFile file) throws NacosException { + RestResult rr = new RestResult(); + if(StringUtils.isNotBlank(namespace)){ + if(persistService.tenantInfoCountByTenantId(namespace) <= 0){ + rr.setCode(500); + rr.setData(false); + rr.setMessage("namespace does not exist"); + return rr; + } + } + List configInfoList = null; + try { + ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes()); + ZipUtils.ZipItem metaDataZipItem = unziped.getMetaDataItem(); + Map metaDataMap = new HashMap<>(); + if(metaDataZipItem != null){ + String metaDataStr = metaDataZipItem.getItemData(); + String[] metaDataArr = metaDataStr.split("\r\n"); + for(String metaDataItem : metaDataArr){ + String[] metaDataItemArr = metaDataItem.split("="); + metaDataMap.put(metaDataItemArr[0], metaDataItemArr[1]); + } + } + List itemList = unziped.getZipItemList(); + if(itemList != null && !itemList.isEmpty()){ + configInfoList = new ArrayList<>(itemList.size()); + for(ZipUtils.ZipItem item : itemList){ + String[] groupAdnDataId = item.getItemName().split("/"); + String group = groupAdnDataId[0]; + String dataId = groupAdnDataId[1]; + + String metaDataId = group + "." + dataId; + metaDataId = metaDataId.substring(0, metaDataId.lastIndexOf(".")) + + "~" + metaDataId.substring(metaDataId.lastIndexOf(".") + 1) + + ".app"; + ConfigInfo ci = new ConfigInfo(); + ci.setTenant(namespace); + ci.setGroup(group); + ci.setDataId(dataId); + ci.setContent(item.getItemData()); + if(metaDataMap.get(metaDataId) != null){ + ci.setAppName(metaDataMap.get(metaDataId)); + } + configInfoList.add(ci); + } + } + } catch (IOException e) { + rr.setCode(500); + rr.setData(false); + rr.setMessage("parsing data failed"); + log.error("parsing data failed", e); + return rr; + } + if (configInfoList == null || configInfoList.isEmpty()) { + rr.setCode(500); + rr.setData(false); + rr.setMessage("data is empty"); + return rr; + } + final String srcIp = RequestUtil.getRemoteIp(request); + String requestIpApp = RequestUtil.getAppName(request); + final Timestamp time = TimeUtils.getCurrentTime(); + persistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, null, time, false); + for (ConfigInfo configInfo : configInfoList) { + EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(), configInfo.getGroup(), + configInfo.getTenant(), time.getTime())); + ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), + configInfo.getTenant(), requestIpApp, time.getTime(), + LOCAL_IP, ConfigTraceService.PERSISTENCE_EVENT_PUB, configInfo.getContent()); + } + rr.setCode(200); + rr.setData(true); + rr.setMessage("import ok"); + return rr; + } } diff --git a/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java b/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java index 6c53e0d91..533dacce5 100755 --- a/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java @@ -25,10 +25,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Timestamp; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import javax.annotation.PostConstruct; @@ -54,6 +51,7 @@ import org.springframework.transaction.*; import org.springframework.transaction.support.DefaultTransactionDefinition; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; +import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; import com.alibaba.nacos.config.server.utils.event.EventDispatcher; import com.google.common.collect.Lists; @@ -1458,7 +1456,6 @@ public class PersistService { * * @param pageNo 页码(必须大于0) * @param pageSize 每页大小(必须大于0) - * @param group * @return ConfigInfo对象的集合 */ public Page findConfigInfoByApp(final int pageNo, @@ -3309,6 +3306,7 @@ public class PersistService { public void batchInsertOrUpdate(List configInfoList, String srcUser, String srcIp, Map configAdvanceInfo, Timestamp time, boolean notify) throws NacosException{ PlatformTransactionManager transactionManager = this.getTransactionTemplate().getTransactionManager(); + assert transactionManager != null; DefaultTransactionDefinition def = new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); TransactionStatus status = transactionManager.getTransaction(def); @@ -3323,6 +3321,36 @@ public class PersistService { ConfigInfo configInfo2Save = new ConfigInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), configInfo.getAppName(), configInfo.getContent()); try { + String extName = configInfo.getDataId().substring(configInfo.getDataId().lastIndexOf(".") + 1).toLowerCase(); + String type = null; + switch (extName){ + case "yml": + case "yaml": + type = "yaml"; + break; + case "txt": + case "text": + type = "text"; + break; + case "json": + type = "json"; + break; + case "xml": + type = "xml"; + break; + case "htm": + case "html": + type = "html"; + break; + case "properties": + type = "Properties"; + break; + } + if (configAdvanceInfo == null) { + configAdvanceInfo = new HashMap<>(); + } + configAdvanceInfo.put("type", type); + addConfigInfoNoTransaction(srcIp, srcUser, configInfo2Save, time, configAdvanceInfo, notify); } catch (DataIntegrityViolationException ive) { // 唯一性约束冲突 updateConfigInfoNoTransaction(configInfo2Save, srcIp, srcUser, time, configAdvanceInfo, notify); @@ -3387,6 +3415,24 @@ public class PersistService { } } + /** + * 根据 tenantId 查询 tenantInfo (namespace)是否存在 + * + * @param tenantId + * @return 根据ID查询到的数据数量 + */ + public int tenantInfoCountByTenantId(String tenantId) { + Assert.hasText(tenantId, "tenantId can not be null"); + String sql = "select count(1) from tenant_info where tenant_id = ?"; + List paramList = new ArrayList<>(); + paramList.add(tenantId); + Integer result = this.jt.queryForObject(sql, paramList.toArray(), Integer.class); + if (result == null) { + return 0; + } + return result.intValue(); + } + static final TenantInfoRowMapper TENANT_INFO_ROW_MAPPER = new TenantInfoRowMapper(); diff --git a/config/src/main/java/com/alibaba/nacos/config/server/utils/ZipUtils.java b/config/src/main/java/com/alibaba/nacos/config/server/utils/ZipUtils.java index 15dd13206..298b2b43b 100644 --- a/config/src/main/java/com/alibaba/nacos/config/server/utils/ZipUtils.java +++ b/config/src/main/java/com/alibaba/nacos/config/server/utils/ZipUtils.java @@ -142,6 +142,35 @@ public class ZipUtils { } } + public static class UnZipResult{ + + private List zipItemList; + + private ZipItem metaDataItem; + + + public UnZipResult(List zipItemList, ZipItem metaDataItem) { + this.zipItemList = zipItemList; + this.metaDataItem = metaDataItem; + } + + public List getZipItemList() { + return zipItemList; + } + + public void setZipItemList(List zipItemList) { + this.zipItemList = zipItemList; + } + + public ZipItem getMetaDataItem() { + return metaDataItem; + } + + public void setMetaDataItem(ZipItem metaDataItem) { + this.metaDataItem = metaDataItem; + } + } + public static byte[] zip(List source){ ByteArrayOutputStream byteOut = null; ZipOutputStream zipOut = null; @@ -176,10 +205,11 @@ public class ZipUtils { return result; } - public static List unzip(byte[] source) { + public static UnZipResult unzip(byte[] source) { ZipInputStream zipIn = null; - List result = new ArrayList<>(); + List itemList = new ArrayList<>(); + ZipItem metaDataItem = null; try { zipIn = new ZipInputStream(new ByteArrayInputStream(source)); ZipEntry entry = null; @@ -192,7 +222,11 @@ public class ZipUtils { while ((offset = zipIn.read(buffer)) != -1) { out.write(buffer, 0, offset); } - result.add(new ZipItem(entry.getName(), out.toString("UTF-8"))); + if(entry.getName().equals(".meta.yml")){ + metaDataItem = new ZipItem(entry.getName(), out.toString("UTF-8")); + } else { + itemList.add(new ZipItem(entry.getName(), out.toString("UTF-8"))); + } } catch (IOException e) { e.printStackTrace(); } finally { @@ -212,7 +246,7 @@ public class ZipUtils { } } } - return result; + return new UnZipResult(itemList, metaDataItem); } diff --git a/test/src/test/java/top/klw8/test/ZipTest.java b/test/src/test/java/top/klw8/test/ZipTest.java deleted file mode 100644 index 65090a8a5..000000000 --- a/test/src/test/java/top/klw8/test/ZipTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package top.klw8.test; - -import java.io.*; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -/** - * @author klw - * @ClassName: ZipTest - * @Description: zip测试 - * @date 2019/5/16 14:22 - */ -public class ZipTest { - - public static void main(String[] args) throws IOException { - // 压缩 - ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); - ZipOutputStream zipOut = new ZipOutputStream(byteOut); - zipOut.putNextEntry(new ZipEntry("DEFAULT_GROUP2/")); - zipOut.putNextEntry(new ZipEntry("DEFAULT_GROUP/test10.yml")); - zipOut.write("abc: 123".getBytes()); - zipOut.putNextEntry(new ZipEntry("DEFAULT_GROUP/test11.yml")); - zipOut.write("def: 456".getBytes()); - zipOut.close(); - FileOutputStream fileOut = new FileOutputStream(new File("E:/test.zip")); - byte[] zipBytes = byteOut.toByteArray(); - fileOut.write(zipBytes); - fileOut.close(); - byteOut.close(); - - //解压 - ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(zipBytes)); - ZipEntry entry = null; - while((entry = zipIn.getNextEntry()) != null){ - System.out.println(entry.getName()); - if(entry.isDirectory()){ - System.out.println("是文件夹"); - } else { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] buffer = new byte[1024]; - int offset = -1; - while ((offset = zipIn.read(buffer)) != -1) { - out.write(buffer, 0, offset ); - } - System.out.println(out.toString("UTF-8")); - out.close(); - } - } - zipIn.close(); - } -}