知識
不管是網(wǎng)站,軟件還是小程序,都要直接或間接能為您產(chǎn)生價值,我們在追求其視覺表現(xiàn)的同時,更側(cè)重于功能的便捷,營銷的便利,運營的高效,讓網(wǎng)站成為營銷工具,讓軟件能切實提升企業(yè)內(nèi)部管理水平和效率。優(yōu)秀的程序為后期升級提供便捷的支持!
您當(dāng)前位置>首頁 » 新聞資訊 » 網(wǎng)站建設(shè) >
Request,Response編碼解碼原理,文件上傳下載和底層IO
發(fā)表時間:2016-10-25
發(fā)布人:葵宇科技
瀏覽次數(shù):29
最近發(fā)現(xiàn)自己基礎(chǔ)很差,自以為學(xué)的不錯的東西,其實缺乏練習(xí)和復(fù)習(xí),基礎(chǔ)十分不扎實。惡補(bǔ)無從下手,只得從以前的具體例子出發(fā),在例子中發(fā)現(xiàn)能力存在的問題,結(jié)合實例復(fù)習(xí)和掌握遺忘的和本來就不扎實沒學(xué)透的知識:
源程序:
表單顯示頁面的Servlet和JSP:
package cn.xbai.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FileUploadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//轉(zhuǎn)發(fā)和重定向的區(qū)別,這里沒設(shè)置編碼,為什么轉(zhuǎn)發(fā)到的jsp頁面可以正常顯示中文?
//-轉(zhuǎn)向jsp那個servlet,其編碼是UTF-8,給response解碼和通知瀏覽器編碼的都是UTF-8
request.getRequestDispatcher("/jsp/index.jsp").forward(request, response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>My JSP 'index.jsp' starting page</title>
</head>
<body>
This is my JSP page. <br>
你好,你好<br>
<form action="${pageContext.request.contextPath }/servlet/FileUploadSubmit" method="post">
姓名:<input type="text" name="name"/><br/>
年齡:<input type="text" name="age"/><br/>
證件類型:<select name="idType">
<option value="1">身份證</option>
<option value="0">其他</option>
</select><br/>
證件號碼:<input type="password" name="idNum"/><br/>
簡歷資料:<input type="file" name="introduction"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
處理提交的Servlet:
package cn.xbai.servlet;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FileUploadSubmit extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 碼表原理-----------------------------------------
*/
request.setCharacterEncoding("UTF-8");//post提交解碼 -因為Socket底層傳輸都是字節(jié)流
//-瀏覽器根據(jù)頁面編碼碼表編碼成字節(jié)流
//復(fù)習(xí)io,socket,tcp/ip,多線程深入,集合,數(shù)據(jù)結(jié)構(gòu)
/*//get:或可在過濾器中攔截,用包裝類重寫request的getParameter方法
String username=request.getParameter("name");//get提交怎樣都是iso-8859-1編碼
//反向獲取字節(jié)碼重新解碼
username=new String(username.getBytes("iso8859-1"),"UTF-8");
*/
/**
* 實驗:中文,瀏覽器提交頁面是以UTF-8提交(也就是按UTF-8編碼傳輸過來):
* 1.request不設(shè)置,response按UTF-8,亂碼
* 2.request設(shè)置UTF-8,response按UTF-8,正常
* 3.request,response都什么也不設(shè)置:涓浗-因request默認(rèn)iso-8859-1解碼,response默認(rèn)iso-8859-1編碼,而瀏覽器因為沒有控制默認(rèn)GBK打開
* 4.接著3,把request設(shè)置成UTF-8,response不設(shè)置:?? 而sysout正常:中國(io字節(jié)流,默認(rèn)GBK編碼解碼?查!-即時查:System.out是PrintStream,PrintStream 打印的所有字符都使用平臺的默認(rèn)字符編碼轉(zhuǎn)換為字節(jié)。
* 在需要寫入字符而不是寫入字節(jié)的情況下,應(yīng)該使用 PrintWriter 類。 那么平臺這邊解碼也應(yīng)該是默認(rèn)字符編碼,對高級語言層面的數(shù)據(jù)(此時是正常的)解碼編碼相同,雖然是GBK,所以不亂嗎
)-因response按iso-8859-1寫入,瀏覽器因為沒有控制,經(jīng)查以GBK打開,自然亂碼-每一步都有事實依據(jù)不是臆測亂說的!
* 5.request,response都設(shè)置UTF-8,只不控制瀏覽器輸出:涓浗-因UTF-8編碼的數(shù)據(jù)用GBK解碼了:查瀏覽器是GBK打開,這和3情形類似
*/
//response.setCharacterEncoding("UTF-8");//寫給response,設(shè)置response編碼-將高級語言級別數(shù)據(jù)編碼成字節(jié)流給response對象
//response.setContentType("text/html;charset=UTF-8");//設(shè)置響應(yīng)頭控制瀏覽器解碼
String name=request.getParameter("name");
System.out.println(name);
//不明白碼表原理,去看源碼!底層io:看看response的getWriter返回是什么流-即時查:
/**
* <pre>
* getWriter
*
* public java.io.PrintWriter getWriter()
* throws java.io.IOException
*
* Returns a PrintWriter object that can send character text to the client. The PrintWriter uses the character encoding returned by getCharacterEncoding(). If the response's character encoding has not been specified as described in getCharacterEncoding (i.e., the method just returns the default value ISO-8859-1), getWriter updates it to ISO-8859-1.
*
* </pre>
* 說明是PrintWriter類型,默認(rèn)設(shè)置成iso-8859-1編碼
*/
//response.getWriter().write(name);//設(shè)置了編碼,就以這個編碼寫入response
/**
* 終極實驗:request正確解碼的情況下,response什么都不設(shè)置,用getOutputStream(它返回一個ServletOutputStream,是OutputStream的直接子類)原始字節(jié)流傳輸:中國,這里name這個String默認(rèn)GBK編碼成字節(jié)流,瀏覽器因為沒有控制也默認(rèn)GBK打開,所以不亂碼
*/
response.getOutputStream().write(name.getBytes());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
我寫的都是我驗證過的,具體原理在代碼和注釋當(dāng)中。
文件上傳的操作:前面form表單頁面設(shè)置成enctype="multipart/form-data"
處理提交的Servlet:
package cn.xbai.servlet;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class FileUploadSubmit extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 文件上傳表單類型--------------------------------------------
* 前面用了multipart/form-data類型,這里這種方法就接收不到了
* 500是服務(wù)器端錯誤:這里因為multipart/form-data類型而無法這樣獲取到,反復(fù)嘗試堅持調(diào)錯才能明白
*/
//String name=request.getParameter("name");
//System.out.println(name);
/**
* 關(guān)于文件的接收操作-------------------------------------------
* 實驗:String默認(rèn)解碼,UTF-8內(nèi)容亂碼,GBK的文件內(nèi)容正常;UTF-8解碼,UTF-8數(shù)據(jù)正常,GBK文件內(nèi)容亂碼
再實驗:request不設(shè)置UTF-8,本來就無法通過request.getParameter接收,所以這里無影響
再實驗:提交jsp頁面改為GBK,這里仍然UTF-8解碼,全部亂碼
再實驗:提交jsp頁面改為GBK,這里GBK解碼,全部正常
結(jié)論:jsp頁面碼表設(shè)置用于瀏覽器傳遞時編碼非文件數(shù)據(jù)(和顯示時解碼?),這里的解碼和編碼一致才不會亂碼
文件以字節(jié)方式存儲在磁盤,存儲時編碼是什么就按什么編碼存儲,如果解碼用不同碼表,就會亂碼,表單選擇文件時以原始字節(jié)流關(guān)聯(lián),原始字節(jié)流傳輸,不存在碼表問題,只是這里解碼需要注意源文件存儲時編碼
因困惑而重復(fù)實驗:jsp頁面UTF-8,這里GBK解碼,GBK文件正常,UTF-8數(shù)據(jù)不正常
那么,最終結(jié)論和解決方案:UTF-8解碼UTF-8數(shù)據(jù),GBK解碼GBK文件,分開判斷
*/
InputStream in=request.getInputStream();
int length=0;
byte[] buffer=new byte[1024];
while((length=in.read(buffer))>0){
System.out.println(new String(buffer,0,length,"GBK"));
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
文件上傳需要注意的問題:
1.上傳文件的中文亂碼
1.1 解決文件的亂碼
ServletFileUpload.setHeaderEncoding("UTF-8")
1.2 解決普通輸入項的亂碼(注意,表單類型為multipart/form-data的時候,設(shè)置request的編碼是無效的)
FileItem.setString("UTF-8"); //解決亂碼
2.在處理表單之前,要記得調(diào)用:
ServletFileUpload.isMultipartContent方法判斷提交表單的類型,如果該方法返回true,則按上傳方式處理,否則按照傳統(tǒng)方式處理表單即可。
3.設(shè)置解析器緩沖區(qū)的大小,以及臨時文件的刪除
設(shè)置解析器緩沖區(qū)的大小 DiskFileItemFactory.setSizeThreshold(1024*1024);
臨時文件的刪除:在程序中處理完上傳文件后,一定要記得調(diào)用item.delete()方法,以刪除臨時文件
4.在做上傳系統(tǒng)時,千萬要注意上傳文件的保存目錄,這個上傳文件的保存目錄絕對不能讓外界直接訪問到。
5.限制上傳文件的類型
在處理上傳文件時,判斷上傳文件的后綴名是不是允許的
6.限制上傳文件的大小
調(diào)用解析器的ServletFileUpload.setFileSizeMax(1024*1024*5);就可以限制上傳文件的大小,如果上傳文件超出限制,則解析器會拋FileUploadBase.FileSizeLimitExceededException異常,程序員通過是否抓到這個異常,進(jìn)而就可以給用戶友好提示。
7.如何判斷空的上傳輸入項
String filename = item.getName().substring(item.getName().lastIndexOf("\\")+1); //""
if(filename==null || filename.trim().equals("")){
continue;
}
8、為避免上傳文件的覆蓋,程序在保存上傳文件時,要為每一個文件生成一個唯一的文件名
public String generateFileName(String filename){
//83434-83u483-934934
return UUID.randomUUID().toString() + "_" + filename;
}
9、為避免在一個文件夾下面保存超過1000個文件,影響文件訪問性能,程序應(yīng)該把上傳文件打散后存儲。
public String generateSavePath(String path,String filename){
int hashcode = filename.hashCode(); //121221
int dir1 = hashcode&15;
int dir2 = (hashcode>>4)&0xf;
String savepath = path + File.separator + dir1 + File.separator + dir2;
File file = new File(savepath);
if(!file.exists()){
file.mkdirs();
}
return savepath;
}
10、監(jiān)聽上傳進(jìn)度
ServletFileUpload upload = new ServletFileUpload(factory);
upload.setProgressListener(new ProgressListener(){
public void update(long pBytesRead, long pContentLength, int pItems) {
System.out.println("當(dāng)前已解析:" + pBytesRead);
}
});
11、在web頁面中添加動態(tài)上傳輸入項
一個考慮全面的文件上傳程序(提交處理,表單頁面同前面):
package cn.xbai.servlet;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.ProgressListener;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
public class FileUploadSubmit extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 文件上傳的步驟
*/
//如果表單類型為multipart/form-data,在servlet中注意不能采用傳統(tǒng)方式獲取數(shù)據(jù)
//并且設(shè)置request編碼無效(request把jsp提交的字節(jié)碼轉(zhuǎn)換過來)
//創(chuàng)建解析工廠
//創(chuàng)建解析器
//調(diào)用解析器解析request,得到保存了所有上傳數(shù)據(jù)的list
//迭代list集合,拿到封裝了每個輸入項的fileItem
//判斷item類型,是普通字段則直接獲取數(shù)據(jù),是上傳文件,則調(diào)用流獲取數(shù)據(jù)寫入本地硬盤
List types=Arrays.asList("jpg","gif","avi","txt");
/**
* 文件上傳框架操作------------------------------------------------
* 循序漸進(jìn):分別解析普通字段和文件數(shù)據(jù)的解決方案,官方fileupload相關(guān)框架
*/
try {
DiskFileItemFactory factory=new DiskFileItemFactory();
factory.setSizeThreshold(1024*1024);
factory.setRepository(new File(this.getServletContext().getRealPath("/temp")));
ServletFileUpload upload=new ServletFileUpload(factory);
upload.setProgressListener(new ProgressListener(){
public void update(long pBytesRead, long pContentLength, int pItems) {
// TODO Auto-generated method stub
System.out.println("當(dāng)前已解析:" + pBytesRead);
}
});
upload.setFileSizeMax(1024*1024*5);
if(!upload.isMultipartContent(request)){
//按照傳統(tǒng)方式獲取表單數(shù)據(jù)
request.getParameter("username");
return;
}
//解決亂碼:
upload.setHeaderEncoding("UTF-8");
List<FileItem> list=upload.parseRequest(request);
for(FileItem item:list){
if(item.isFormField()){
//為普通輸入項
String inputName=item.getFieldName();
String inputValue=item.getString("UTF-8");//這樣解碼
//這樣先從亂碼還原再重新解碼也可以
//inputValue = new String(inputValue.getBytes("iso8859-1"),"UTF-8");
System.out.println(inputName + "=" + inputValue);
}else{
String filename=item.getName().substring(item.getName().lastIndexOf("\\")+1);
if(filename==null || filename.trim().equals("")){
continue;
}
/*String ext = filename.substring(filename.lastIndexOf(".")+1);
if(!types.contains(ext)){
request.setAttribute("message", "本系統(tǒng)不支持" + ext + "這種類型");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}*/
//如果文件大小超過指定值,則存儲到一個臨時文件中,那么這里的流是讀臨時文件
InputStream in=item.getInputStream();
int len=0;
byte buffer[]=new byte[1024];
String saveFileName=generateFileName(filename);
//!!!注意:上傳文件的目錄一定不能讓外界直接訪問到------->放到WEB-INF目錄下!!
//如果設(shè)置在應(yīng)用目錄下的一個目錄中,用戶上傳一個破壞服務(wù)器的jsp,再猜路徑訪問這個jsp執(zhí)行,會有安全問題
String savepath=generateSavePath(this.getServletContext().getRealPath("/WEB-INF/upload"),saveFileName);
//分隔符:windows下開發(fā)linux下部署,要用兼容的,調(diào)用File的一個方法
FileOutputStream out=new FileOutputStream(savepath+File.separator+saveFileName);
//讀取,寫入
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
//別忘了關(guān)流:應(yīng)該在try-catch-finally里的
try {
if(in!=null){
in.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}finally{
try {
if(out!=null){
out.close();
}
} catch (Exception e) {
// TODO: handle exception
throw new RuntimeException(e);
}
}
item.delete();//刪除臨時文件,注意一定放在流關(guān)閉之后,不然刪不掉!
}
}
}catch (FileUploadBase.FileSizeLimitExceededException e) {
request.setAttribute("message", "文件大小不能超過5m");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}catch (Exception e) {
throw new RuntimeException(e);
}
request.setAttribute("message", "上傳成功!!");
request.getRequestDispatcher("/message.jsp").forward(request, response);
}
public String generateSavePath(String path,String filename){
int hashcode=filename.hashCode();
//取低4位
int dir1=hashcode&15;//以后這樣的機(jī)巧算法要自己懂得琢磨,利用底層基礎(chǔ)
int dir2=(hashcode>>4)&0xf;//右移4位后取低4位
String savepath=path+File.separator+dir1+File.separator+dir2;
File file=new File(savepath);
//別忘了創(chuàng)建這個目錄
if(!file.exists()){
file.mkdirs();//創(chuàng)建級聯(lián)目錄
}
return savepath;
}
public String generateFileName(String filename){
return UUID.randomUUID().toString()+"_"+filename;
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
文件下載:
列出所有文件:
package cn.xbai.servlet;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ListFileServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String path=this.getServletContext().getRealPath("/download");
File ddir=new File(path);
List<File> fileList=new ArrayList<File>();//不要忘了初始化,否則空指針異常
listAllFiles(ddir,fileList);
Map<String,String> fileMap=new LinkedHashMap<String,String>();//沒順序是一開始這個Map無序造成的!
for(File file:fileList){
fileMap.put(file.getName(),file.getPath());
}
request.setAttribute("fileMap", fileMap);
request.getRequestDispatcher("/WEB-INF/jsp/downloadlist.jsp").forward(request, response);
}
private void listAllFiles(File ddir,List<File> fileList) {//因為有遞歸,這里用參數(shù)傳入,遞歸調(diào)用中傳入這同一個參數(shù),達(dá)到一個集合存儲所有遞歸結(jié)果且有遞歸順序
// TODO Auto-generated method stub
if(!ddir.isFile()){
//fileList.add(ddir);
File children[]=ddir.listFiles();
for(File f:children){
listAllFiles(f,fileList);
}
}else{
fileList.add(ddir);//遞歸出口
}
/*
* 下面的思路有問題,ddir就應(yīng)該判斷了,而不是遍歷它的子元素才開始判斷,造成遞歸出的順序混亂!
//API文檔引用:如果此抽象路徑名不表示一個目錄,那么此方法將返回 null。
//否則返回一個 File 對象數(shù)組,每個數(shù)組元素對應(yīng)目錄中的每個文件或目錄。
File[] files = ddir.listFiles();//是前面list方法返回的只是文件名而不是路徑,這里才遞歸不出來
//遞歸出口:到最末端文件,list為空,執(zhí)行完方法后自然不再遞歸,因為下面沒文件了
if(files!=null){//為了有遞歸出口且順利完成遞歸并返回,不能用try,catch,而要有一個出口判斷條件
for (File file : files) {
if (file.exists() && file.isFile()) {
fileList.add(file);
} else {
fileList.add(file);
listAllFiles(file, fileList);
}
}
}
*/
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Jsp</title>
</head>
<body>
有以下資源供您下載:<br/>
<c:forEach items="${requestScope.fileMap}" var="entry">
<!-- 下載的文件名防中文亂碼,用url標(biāo)簽(標(biāo)簽知識待復(fù)習(xí)待查) -->
<c:url var="url" value="/servlet/DownloadServlet">
<c:param name="filename" value="${entry.value}"></c:param>
</c:url>
${entry.key }<a href="${url }">下載</a><br/>
</c:forEach><!-- 去Servlet中獲取文件絕對地址,設(shè)置下載相關(guān),下載文件 -->
</body>
</html>
下載處理Servlet:
package cn.xbai.servlet;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class DownloadServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//要下載文件的磁盤全路徑
String filepath=request.getParameter("filename");
//get參數(shù),中文需要還原字節(jié)碼重新解碼成UTF-8
filepath=new String(filepath.getBytes("iso8859-1"),"UTF-8");
File file=new File(filepath);
if(!file.exists()){
request.setAttribute("message", "對不起,您要下載的資源已被刪除");
request.getRequestDispatcher("/message.jsp").forward(request, response);
return;
}
String filename=filepath.substring(filepath.lastIndexOf("\\"));
//通知瀏覽器以下載打開
//文件名需要用URLEncoder編碼
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(filename,"UTF-8"));
//獲取文件流
FileInputStream in=new FileInputStream(file);
int len=0;
byte buffer[]=new byte[1024];
OutputStream out=response.getOutputStream();
while((len=in.read(buffer))>0){
out.write(buffer,0,len);
}
try {
if(in!=null){
in.close();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
//String path=this.getServletContext().getRealPath("/download");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
相關(guān)案例查看更多
相關(guān)閱讀
- 昆明小程序開發(fā)
- 云南小程序哪家好
- 貴州小程序開發(fā)
- 網(wǎng)站建設(shè)案例
- flex
- 云南軟件設(shè)計
- 網(wǎng)站收錄
- 曲靖小程序開發(fā)
- 網(wǎng)絡(luò)公司電話
- 百度小程序開發(fā)
- 紅河小程序開發(fā)
- 昆明網(wǎng)站設(shè)計
- 網(wǎng)站建設(shè)制作
- 云南建設(shè)廳網(wǎng)站
- 云南小程序開發(fā)制作
- 網(wǎng)站建設(shè)靠譜公司
- 小程序開發(fā)聯(lián)系方式
- 網(wǎng)站開發(fā)公司哪家好
- 網(wǎng)站建設(shè)公司地址
- 云南網(wǎng)站建設(shè)哪家公司好
- vue開發(fā)小程序
- 微分銷
- 云南軟件定制公司
- 云南建站公司
- 搜索引擎自然排名
- 云南電商網(wǎng)站建設(shè)
- 大理小程序開發(fā)
- asp網(wǎng)站
- 支付寶小程序被騙
- 云南省建設(shè)廳網(wǎng)站官網(wǎng)