Spring MVC 實現Excel的導入導出功能(2:Excel的導入優化和Excel的導出)

Excel的導入V2優化版

有些時候文件上傳這一步驟由前端來處理,只將上傳后的 URL 傳輸給后端(可以參考上一文中的圖片上傳功能),也就是導入請求中并不會直接處理 MultipartFile 對象,而是通過文件 URL 將其下載,之后進行文件流處理,具體過程如下:

?

點擊 “ 導入V2 ” 按鈕,與 V1 按鈕不同,此時出現的不是文件選擇框,而是一個上傳文件的彈框,點擊彈框里的 “ 上傳 Excel 文件 ” 按鈕才會出現文件選擇框,選擇正確的文件后點擊確定,請求完成后同 V1 版本的導入功能效果一樣。

前端實現

HTML頁面

<!-- Main content --><div class="content"><div class="row"><div class="col-12"><div class="card"><div class="card-body"><div class="grid-btn"><button class="btn btn-info" onclick="userAdd()"><i class="fa fa-plus"></i>&nbsp;新增</button><button class="btn btn-success" onclick="userEdit()"><i class="fa fa-plus"></i>&nbsp;編輯</button><button class="btn btn-danger" onclick="deleteUser()"><i class="fa fa-remove"></i>&nbsp;刪除</button><button class="btn btn-default" id="importV1Button"><i class="fa fa-upload"></i>&nbsp;導入ExcelV1</button><button class="btn btn-default" onclick="importV2()"><iclass="fa fa-upload"></i>&nbsp;導入ExcelV2</button><button class="btn btn-primary" onclick="window.location.href='/users/export'"><i class="fa fa-download"></i> 導出</button></div><table id="jqGrid" class="table table-bordered"></table><div id="jqGridPager"></div></div></div></div></div></div>
</div>

HTML模態框(這里用的是Custombox)

<!--導入Excel模態框--><div class="content"><div class="modal" id="importV2Modal" tabindex="-1" role="dialog" aria-labelledby="importV2ModalLabel"><div class="modal-dialog" role="document"><div class="modal-content"><div class="modal-header"><h6 class="modal-title" id="importV2ModalLabel">用戶導入</h6></div><div class="modal-body"><div class="form-group"><input type="hidden" id="fileUrl" value=""><div class="col-sm-10"><a class="btn btn-default" id="uploadExcelV2"><i class="fa fa-file">上傳Excel文件</i></a></div></div></div><div class="modal-footer"><button type="button" class="btn btn-default" id="cancelImportV2">取消</button><button type="button" class="btn btn-primary" id="importV2Button">確認</button></div></div></div></div></div>

導入的JS

// 用戶導入V2
function importV2() {var modal = new Custombox.modal({content: {effect: 'fadein',target: '#importV2Modal'}});modal.open();
}
    new AjaxUpload('#uploadExcelV2', {action: 'upload/file',name: 'file',autoSubmit: 'true',responseType: 'json',onSubmit: function (file, extension) {if (!(extension && /^(xlsx)$/.test(extension.toLowerCase()))) {alert('只支持xlsx格式的文件!', {icon: "error",});return false;}},onComplete: function (file, r) {if (r.resultCode == 200) {$("#fileUrl").val(r.data);$("#uploadExcelV2").attr('class','btn-info');alert("上傳Excel成功,請點擊確認添加數據");return false;} else {alert(r.message);}}});
});

這里還是通過上傳獲取文件的url再通過流處理轉換成File類型并把對應的Excel下載到服務器,其他的和V1版本是一樣的

當點擊確定導入的時候,如果之前上傳沒錯,再訪問一次后臺

$("#importV2Button").click(function () {var fileUrl = $("#fileUrl").val();$.ajax({type: 'post',dataType: 'json',url: 'users/importV2?fileUrl=' + fileUrl,contentType:'application/json',success:function (result) {if (result.resultCode==200){closeModal();reload();alert("成功導入"+result.data+"條記錄!");}else {closeModal();alert(result.message);};},error:function () {reset();alert("操作失敗!");}});
});

后端邏輯

控制層

package com.ssm.demo.controller;import com.ssm.demo.common.Result;
import com.ssm.demo.common.ResultGenerator;
import com.ssm.demo.controller.enums.UploadFileTypeEnum;
import com.ssm.demo.utils.FileUtil;
import org.apache.commons.io.FileUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;import static com.ssm.demo.common.Constants.FILE_PRE_URL;/*** Created by 13 on 2017/7/17.*/
@Controller
@RequestMapping("/upload")
public class UploadFileController {/*** 通用 文件上傳接口(可以上傳圖片、視頻、excel等文件,具體格式可在UploadFileTypeEnum中進行配置)** @return*/@RequestMapping(value = "/file", method = RequestMethod.POST)@ResponseBodypublic Result uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {ServletContext sc = request.getSession().getServletContext();String type = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".") + 1, file.getOriginalFilename().length());String fileName = null;UploadFileTypeEnum uploadFileTypeEnum = UploadFileTypeEnum.getFileEnumByType(type);if (uploadFileTypeEnum == UploadFileTypeEnum.ERROR_TYPE) {//格式錯誤則不允許上傳,直接返回錯誤提示return ResultGenerator.genFailResult("請檢查文件格式!");} else {//生成文件名稱通用方法SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");Random r = new Random();StringBuilder tempName = new StringBuilder();tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(".").append(type);fileName = tempName.toString();}try {String dir = sc.getRealPath("/upload");FileUtils.writeByteArrayToFile(new File(dir, fileName), file.getBytes());} catch (IOException e) {//文件上傳異常return ResultGenerator.genFailResult("文件上傳失敗!");}Result result = ResultGenerator.genSuccessResult();//返回文件的全路徑StringBuilder fileUrl = new StringBuilder();fileUrl.append(FILE_PRE_URL).append("/upload/").append(fileName);result.setData(fileUrl.toString());return result;}/*** @param chunks 當前所傳文件的分片總數* @param chunk  當前所傳文件的當前分片數* @return* @Description: 大文件上傳前分片檢查* @author: 13*/@ResponseBody@RequestMapping(value = "/checkChunk")public Result checkChunk(HttpServletRequest request, String guid, Integer chunks, Integer chunk, String fileName) {try {String uploadDir = FileUtil.getRealPath(request);String ext = fileName.substring(fileName.lastIndexOf("."));// 判斷文件是否分塊if (chunks != null && chunk != null) {//文件路徑StringBuilder tempFileName = new StringBuilder();tempFileName.append(uploadDir).append(File.separator).append("temp").append(File.separator).append(guid).append(File.separator).append(chunk).append(ext);File tempFile = new File(tempFileName.toString());//是否已存在分片,如果已存在分片則返回SUCCESS結果if (tempFile.exists()) {return ResultGenerator.genSuccessResult("分片已經存在!跳過此分片!");}}} catch (Exception ex) {ex.printStackTrace();return ResultGenerator.genFailResult("error");}return ResultGenerator.genNullResult("不存在分片");}/*** @param chunks 當前所傳文件的分片總數* @param chunk  當前所傳文件的當前分片數* @return* @Description: 大文件分片上傳* @author: 13*/@ResponseBody@RequestMapping(value = "/files")public Result upload(HttpServletRequest request, String guid, Integer chunks, Integer chunk, String name, MultipartFile file) {String filePath = null;//上傳存儲路徑String uploadDir = FileUtil.getRealPath(request);//后綴名String ext = name.substring(name.lastIndexOf("."));StringBuilder tempFileName = new StringBuilder();//等價于 uploadDir + "\\temp\\" + guid + "\\" + chunk + exttempFileName.append(uploadDir).append(File.separator).append("temp").append(File.separator).append(guid).append(File.separator).append(chunk).append(ext);File tempFile = new File(tempFileName.toString());// 判斷文件是否分塊if (chunks != null && chunk != null) {//根據guid 創建一個臨時的文件夾if (!tempFile.exists()) {tempFile.mkdirs();}try {//保存每一個分片
                file.transferTo(tempFile);} catch (Exception e) {e.printStackTrace();}//如果當前是最后一個分片,則合并所有文件if (chunk == (chunks - 1)) {StringBuilder tempFileFolder = new StringBuilder();//等價于 uploadDir + "\\temp\\" + guid + File.separatortempFileFolder.append(uploadDir).append(File.separator).append("temp").append(File.separator).append(guid).append(File.separator);String newFileName = FileUtil.mergeFile(chunks, ext, tempFileFolder.toString(), request);filePath = "upload/chunked/" + newFileName;}} else {//不用分片的文件存儲到files文件夾中StringBuilder destPath = new StringBuilder();destPath.append(uploadDir).append(File.separator).append("files").append(File.separator);String newName = System.currentTimeMillis() + ext;// 文件新名稱try {FileUtil.saveFile(destPath.toString(), newName, file);} catch (IOException e) {e.printStackTrace();}filePath = "upload/files/" + newName;}Result result = ResultGenerator.genSuccessResult();result.setData(filePath);return result;}
}

FileUtil工具類

package com.ssm.demo.utils;import org.apache.commons.io.FileUtils;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.UUID;public class FileUtil {/*** 轉換MultipartFile對象為java.io.File類型** @param multipartFile* @return*/public static File convertMultipartFileToFile(MultipartFile multipartFile) {File result = null;try {/*** UUID.randomUUID().toString()是javaJDK提供的一個自動生成主鍵的方法。* UUID(Universally Unique Identifier)全局唯一標識符,是指在一臺機器上生成的數字,* 它保證對在同一時空中的所有機器都是唯一的,是由一個十六位的數字組成,表現出來的形式。* 由以下幾部分的組合:當前日期和時間(UUID的第一個部分與時間有關,如果你在生成一個UUID之后,* 過幾秒又生成一個UUID,則第一個部分不同,其余相同),時鐘序列,* 全局唯一的IEEE機器識別號(如果有網卡,從網卡獲得,沒有網卡以其他方式獲得),* UUID的唯一缺陷在于生成的結果串會比較長。*** File.createTempFile和File.createNewFile()的區別:*  后者只是創建文件,而前者可以給文件名加前綴和后綴*///這里對生成的文件名加了UUID隨機生成的前綴,后綴是nullresult = File.createTempFile(UUID.randomUUID().toString(), null);multipartFile.transferTo(result);result.deleteOnExit();} catch (Exception e) {e.printStackTrace();}return result;}/*** 根據url獲取文件對象** @param fileUrl* @return*/public static File downloadFile(String fileUrl) {File result = null;try {result = File.createTempFile(UUID.randomUUID().toString(), null);URL url = new URL(fileUrl);URLConnection connection = url.openConnection();connection.setConnectTimeout(3000);BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(result));byte[] car = new byte[1024];int l = 0;while ((l = bis.read(car)) != -1) {bos.write(car, 0, l);}bis.close();bos.close();} catch (Exception e) {e.printStackTrace();}return result;}/*** @param request* @return*/public static String getRealPath(HttpServletRequest request) {ServletContext sc = request.getSession().getServletContext();String uploadDir = sc.getRealPath("/upload");return uploadDir;}public static boolean saveFile(String savePath, String fileFullName, MultipartFile file) throws IOException {File uploadFile = new File(savePath + fileFullName);FileUtils.writeByteArrayToFile(new File(savePath, fileFullName), file.getBytes());return uploadFile.exists();}public static String mergeFile(int chunksNumber, String ext, String uploadFolderPath,HttpServletRequest request) {//合并分片流String mergePath = uploadFolderPath;String destPath = getRealPath(request);// 文件路徑String newName = System.currentTimeMillis() + ext;// 文件新名稱
        SequenceInputStream s;InputStream s1;try {s1 = new FileInputStream(mergePath + 0 + ext);String tempFilePath;InputStream s2 = new FileInputStream(mergePath + 1 + ext);s = new SequenceInputStream(s1, s2);for (int i = 2; i < chunksNumber; i++) {tempFilePath = mergePath + i + ext;InputStream s3 = new FileInputStream(tempFilePath);s = new SequenceInputStream(s, s3);}//分片文件存儲到/upload/chunked目錄下StringBuilder filePath = new StringBuilder();filePath.append(destPath).append(File.separator).append("chunked").append(File.separator);saveStreamToFile(s, filePath.toString(), newName);// 刪除保存分塊文件的文件夾
            deleteFolder(mergePath);} catch (FileNotFoundException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}return newName;}private static boolean deleteFolder(String mergePath) {File dir = new File(mergePath);File[] files = dir.listFiles();if (files != null) {for (File file : files) {try {file.delete();} catch (Exception e) {e.printStackTrace();}}}return dir.delete();}private static void saveStreamToFile(SequenceInputStream inputStream, String filePath, String newName)throws Exception {File fileDirectory = new File(filePath);synchronized (fileDirectory) {if (!fileDirectory.exists()) {if (!fileDirectory.mkdir()) {throw new Exception("文件夾創建失敗,路徑為:" + fileDirectory);}}if (!fileDirectory.exists()) {if (!fileDirectory.mkdir()) {throw new Exception("文件夾創建失敗,路徑為:" + fileDirectory);}}}OutputStream outputStream = new FileOutputStream(filePath + newName);byte[] buffer = new byte[1024];int len = 0;try {while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);outputStream.flush();}} catch (Exception e) {e.printStackTrace();throw e;} finally {outputStream.close();inputStream.close();}}
}

其他的和V1版本是一樣的

mapper

  <update id="deleteUser">update tb_admin_user set is_deleted = 1where id in<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach></update>

效果展示

Excel的導出實現

前端實現

    <button class="btn btn-primary"     onclick="window.location.href='/users/export'"><i class="fa fa-download"></i> 導出</button>

點擊的時候會直接訪問到Controller層來獲取下載

后端邏輯

?

/*** 導出功能*/@RequestMapping(value = "/export", method = RequestMethod.GET)public void exportUsers(HttpServletRequest request, HttpServletResponse response) {List<AdminUser> userList = adminUserService.getUsersForExport();//單元格表頭String[] excelHeader = {"用戶id", "用戶名", "賬號狀態", "添加時間"};//字段名稱String[] fileds = {"userId", "userName", "status", "createTime"};//單元格寬度內容格式int[] formats = {4, 2, 1, 1};//單元格寬度int[] widths = {256 * 14, 512 * 14, 256 * 14, 512 * 14};try {List<Map<String, Object>> excelData = new ArrayList<Map<String, Object>>();SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");if (CollectionUtils.isNotEmpty(userList)) {for (AdminUser user : userList) {Map<String, Object> map = new HashMap<>();map.put("userId", user.getId());map.put("userName", user.getUserName());map.put("status", user.getIsDeleted() == 0 ? "正常賬號" : "廢棄賬號");map.put("createTime", formatter.format(user.getCreateTime()));excelData.add(map);}}String excelName = "用戶數據_" + System.currentTimeMillis();PoiUtil.exportFile(excelName, excelHeader, fileds, formats, widths, excelData, request, response);} catch (Exception e) {e.printStackTrace();}}

?

注:這里的一些工具類在上一篇寫過了

?

轉載于:https://www.cnblogs.com/xiaowangtongxue/p/10747393.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536521.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536521.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536521.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

計算機系統應用屬于ei,2018年度中心成員發表論文清單(SCI、EI收錄)

序號論文名稱作者發表時間發表刊物名及ISSN號論文類別(SCI(請寫明幾區)、EI、AB類刊物、CSCD)1Power Allocation Study for Non-Orthogonal Multiple Access Networks With Multicast-Unicast TransmissionZheng Yang , Member, IEEE, Jamal Ahmed Hussein , Peng Xu , Member,…

ts基礎總結

ts有什么用 類型檢查, 擁抱es6&#xff0c;支持部分的esNext草案&#xff0c;直接編譯到原生js、引入新的語法糖 為什么用ts TypeScript的設計目的應該是解決JavaScript的“痛點”&#xff1a;弱類型和沒有命名空間&#xff0c;導致很難模塊化&#xff0c;不適合開發大型程序。…

CentOS7啟用SELinux和Firewall修改ssh端口號

基本信息 CentOS :CentOS Linux release 7.6.1810 (Core)SELinux:enforcedFirewall:enforcing生成ssh密鑰對 生成密鑰對 ssh-keygen -t rsa -b 2048 #默認存放的位置是/home/user/.ssh&#xff0c;使用的是公鑰id_rsa.pub從服務器中將私鑰復制到本機或者使用本地生成的密鑰對&a…

華為智能手環智能手表軟件測試,一塊智能手表的測試之旅,揭秘華為運動健康科學實驗室...

隨著消費者對健康生活的日益關注&#xff0c;隨之而來的是智能可穿戴設備的蓬勃發展。一個手環&#xff0c;一個智能手表&#xff0c;都可以為消費者提供諸如心率&#xff0c;步數相關的數據統計。而更進階的設備&#xff0c;則能為用戶提供系統的運動解決方案以及監控人體健康…

Flutter 使用動畫播放一組圖片

請支持原文&#xff1a;tryenough.com/images-anim… 效果如下圖&#xff1a; 代碼 import package:flutter/material.dart; import package:sprintf/sprintf.dart; //這個是一個拼接字符串的flutter庫&#xff0c;主要是為了使用方便&#xff0c;你可以選擇不使用&#xff0c…

軟件測試都有哪些證書,軟件測試都有哪些證書呀?有用嗎?

OYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學習論壇 www.oywp.netOYWP學…

超低頻測試信號產生電路軟件流程圖,一種0_20Hz超低頻信號發生器的設計與實現...

。。第22卷第4期增刊           儀 器 儀 表 學 報             2001年8月一種0&#xff5e;20Hz超低頻信號發生器的設計與實現馬彥恒 郭 利 于久恩 (軍械工程學院 石家莊 050003)摘要 本文介紹了一種采用了主—從式雙CPU結構,頻率和幅度都…

datastage 使用小結

轉載于:https://www.cnblogs.com/Guhan-xue/p/10758663.html

Teams 的邏輯架構與語音解決方案 - Official Posters Download

意外收獲了前幾天&#xff08;0420&#xff09;剛剛更新出來的Teams架構海報&#xff0c;分享給大家下載 Microsoft Teams IT architecture and telephony solutions postershttps://docs.microsoft.com/en-us/microsoftteams/teams-architecture-solutions-posters 接下來跟大…

ug不能自動啟動服務器,沒有足夠的權限啟動系統服務解決方法

沒有足夠的權限啟動系統服務解決方法UG的安裝要注意兩個問題&#xff0c;一個是安裝路徑不能有中文(包括ug文件存儲路徑也不能含有中文),二是許可證的安裝&#xff0c;win7裝ug問題多數出在“許可證”的安裝過程&#xff0c;你的那個報警是&#xff1a;就是說你把UGII_TMP_DIR設…

css直接子元素怎么用,CSS 子元素選擇器使用實例

與后代選擇器相比&#xff0c;子元素選擇器只能選擇作為某元素子元素的元素。子元素選擇器使用大于號">"做為連接符。如果您不希望選擇任意的后代元素&#xff0c;而是希望縮小范圍&#xff0c;只選擇某個元素的子元素&#xff0c;請使用子元素選擇器子元素選擇器…

C# Collection was modified;enumeration operation may not execute

一、問題描述 在做 數組、列表、集合遍歷時&#xff0c;可能我們會遇見這個問題。Collection was modified;enumeration operation may not execute &#xff0c;翻譯的中文意思&#xff1a;集合已修改&#xff1b;枚舉操作可能無法執行。 二、解決方案 就是在遍歷時&#xff0…

xp系統安裝金蝶k3服務器配置,XP單機版安裝金蝶K3的13.1版本號,金蝶K3Wise安裝步驟,安裝成功...

要注意先安裝IIS。再安裝VS2008。我們會常常在控制面板里找不到“應用程序server”這個項目。我們須要依照以下的步驟來操作就會Ok。1.下載IIS6&#xff0c;放置到D盤根文件夾。2.在執行里輸入&#xff1a;c:\Windows\inf\sysoc.inf即打開找到 [Components]段。加入&#xff1a…

通過django 執行命令或者腳本

1、執行django命令 下文的project1為project名&#xff0c;app1為app名 在django project 下執行&#xff1a;python manage.py shell # 可以在Python console里執行Python命令或者包含django的命令 > from app1.models import User > user User.objects.get(name"…

服務器kvm切換器維修,KVM切換器常見問題

數據中心在使用kvm開關的過程中有時會遇到一些使用問題。如果他們不能及時解決問題&#xff0c;就會影響數據中心的有效管理。小編輯總結了一些常見的問題并給出了詳細的答案。我希望能幫助每一個人。經常問的問題和答案&#xff1a;i.用于初始連接的kvm轉換器&#xff0c;kvm開…

Zulip 2.0.3 發布,功能強大的群組聊天軟件

Zulip 2.0.3 發布了&#xff0c;Zulip 是一個強大的開源群組聊天軟件。 用 Python 編寫&#xff0c;使用 Django 框架&#xff0c;支持通過會話流的私人消息和群聊。Zulip 還支持快速搜索、拖放文件上傳、圖像預覽、組私人消息、可聽通知、錯過電子郵件消息提醒&#xff0c;桌面…

ifix怎么裝服務器系統上,ifix服務器和客戶端配置

ifix服務器和客戶端配置 內容精選換一換準備好服務端和客戶端&#xff0c;根據組網規劃完成物理服務器的物理組網。本文檔中以3臺客戶端和3臺TaiShan服務器作為服務端為例。本次部署流程中以3臺客戶端節點和3臺TaiShan 200 服務器(型號2280)作為存儲節點&#xff0c;網絡包含前…

接口測試工具Postman(轉)

接口測試工具Postman Postman是一款功能強大的HTTP調試與模擬插件&#xff0c;不僅可以調試簡單的CSS、HTML、腳本等網頁的基本信息&#xff0c;它還可以發送幾乎所有類型的HTTP請求。Postman適用于不同的操作系統&#xff0c;Mac、WindowsX32、Windows X64、Linux系統等。本篇…

洛谷 P1372 又是畢業季I

可能所有的數論題都是這樣玄學.... 題目鏈接&#xff1a;https://www.luogu.org/problemnew/show/P1372 這道題通過暴力的枚舉可以發現是不可做的&#xff08;當然我也不會做&#xff09; 然后就有了這樣一個思路&#xff1a; 這道題就是求&#xff1a;從1~n中取k個數&#xff…

查看游戲服務器ip地址網站,如何查看游戲服務器IP地址

如何盡量使網絡游戲避免卡機呢&#xff1f;高帶寬&#xff0c;短延時&#xff0c;丟包率這是大家都知道的&#xff0c;還有重要的一點大家容易忽視&#xff0c;卡機與選擇的游戲分區有著密切的聯系&#xff0c;選擇合適的游戲服務器(南北電信運營商之分)最為重要。1、先選擇一個…