728x90
300x250

[JSP] 24. jQuery와 Ajax 멀티 파일 업로드(개선), POST전송, JSON - 구성하기(1)


이번에 게시할 프로젝트는 jQuery, Ajax, 파일 업로드, POST전송, JSON 구성하기라는 거창한 제목이긴 하지만, 실제로는 기능별로 분리하여 작업한 결과이다.

이 글은 기존에 게시글에서 작성한 업로드 기능에 "토큰 인증 방식", "파라메터 문자열 분석 및 분리" 등의 기능을 추가하였다.

아무래도 업로드 기능이니깐 그냥 업로드 하는 것보다는 토큰 하나 넣어주는 게 좋지 않겠냐는 생각이다.


1. [JSP] 12. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(1), 2020-09-24

https://yyman.tistory.com/1414

2. [JSP] 13. Jsp/Servlet(MVC) Maven 기반의 다중 파일 업로드, 다운로드 구현(2), 2020-09-24
https://yyman.tistory.com/1415

3. [JSP] 21. Jsp/Servlet(MVC) Maven 기반의 다중 업로드, 다운로드, 삭제 구현(1), 2020-10-01

https://yyman.tistory.com/1436




그림 1. COS(Servlet)


이거는 오래되서 미지원이다. (일부 인터넷 글에 따르면, 사용중이더라 이런 글들이 있는데 태스트를 해본 바로는 오류가 발생하고 안 된다.

(구체적으로 servlet 패키지에서 충돌남)


* IDE: Spring Tool-Suite 4.4 4-4.7.2. RELEASE (Eclipse)

* MVN(pom.xml)

   - javax.servlet-api 4.0.1 (https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api)

   - json - 20200518 (https://mvnrepository.com/artifact/org.json/json)

   - commons-io - 2.8.0 (https://mvnrepository.com/artifact/commons-io/commons-io)

   - commons-fileupload - 1.4 (https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload)

* Javascript: jquery-3.5.1.js


DB 자체를 제거했으며, 이해를 돕기 위해서 매우 순수한 구조로 작성되었다.


자료구조에서 LinkedList에 해당하는 자바의 ArrayList와 Map를 파일 업로드 부분에 적용하였음.



1. 결과


이번에 만들 작업에 관한 결과이다. 결과를 보고 무엇을 만들지 생각하는 방법을 터득했으면 좋겠다.



그림 1. 작업 결과



그림 2. 작업 결과 - 출력된 HTML 소스(1)



그림 3. 작업 결과 - 출력된 HTML 소스(2)



그림 4. 작업 결과(2) - ListJSONController.java



그림 5. 작업결과(3) - 파일 업로드 기능(1)



그림 6. 작업결과(4) - 파일 업로드 기능(2)



그림 6-1. JSON - 결과(1)



그림 6-2. JSON - 결과(2)



그림 6-3. JSON - 결과(3)



그림 6-4. JSON - 결과(4)




2. 프로젝트 구성도


만들어야 할 프로그램들이다. 조금 양이 많다.



그림 7. 프로젝트 구성




3. 프로젝트 생성


프로젝트 생성하는 방법에 대해서 소개하도록 하겠다.



그림 8. 프로젝트 생성하기(1)


File을 누른다.

New-> Maven Project를 클릭한다.

(1단계 중략 - Next만 눌러도 되는 부분)



그림 9. 프로젝트 생성하기(2)


webapp를 검색한다.

org.apache.maven.archetypes    |  maven-archetype-webapp  1.4을 선택한다.

Next를 누른다.



그림 10. 프로젝트 생성하기(3)


Group Id와 Artifact Id를 입력한다.

Finish를 누른다.



4. 프로젝트 속성 - Build Path, Project Factes 설정하기


자바 버전을 변경할 것이다.



그림 11. 프로젝트 선택 후의 마우스 오른쪽 버튼 메뉴


프로젝트를 선택한 후, 마우스 오른쪽 버튼으로 클릭한다.

Properties를 클릭한다.



그림 12. Java Build Path


Java Build Path를 클릭한다.

JRE System Library 버전을 14로 바꿔준다. [Edit 클릭 -> JRE -> Java SE -14]




그림 13. Project Factes


Project Factes를 클릭한다.

Java의 버전을 14로 바꿔준다.



5. pom.xml 수정하기


pom.xml 수정에 관한 것이다.


<?xml version="1.0" encoding="UTF-8"?>


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

  <modelVersion>4.0.0</modelVersion>


  <groupId>com.webedu</groupId>

  <artifactId>exampleAjax</artifactId>

  <version>0.0.1-SNAPSHOT</version>

  <packaging>war</packaging>


  <name>exampleAjax Maven Webapp</name>

  <!-- FIXME change it to the project's website -->

  <url>http://www.example.com</url>


  <properties>

    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven.compiler.source>1.7</maven.compiler.source>

    <maven.compiler.target>1.7</maven.compiler.target>

  </properties>


  <dependencies>

    <dependency>

      <groupId>junit</groupId>

      <artifactId>junit</artifactId>

      <version>4.11</version>

      <scope>test</scope>

    </dependency>

    <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->

<dependency>

    <groupId>javax.servlet</groupId>

    <artifactId>javax.servlet-api</artifactId>

    <version>4.0.1</version>

    <scope>provided</scope>

</dependency>

<!-- https://mvnrepository.com/artifact/org.json/json -->

<dependency>

    <groupId>org.json</groupId>

    <artifactId>json</artifactId>

    <version>20200518</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->

<dependency>

    <groupId>commons-io</groupId>

    <artifactId>commons-io</artifactId>

    <version>2.8.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->

<dependency>

    <groupId>commons-fileupload</groupId>

    <artifactId>commons-fileupload</artifactId>

    <version>1.4</version>

</dependency>

  </dependencies>


  <build>

    <finalName>exampleAjax</finalName>

    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->

      <plugins>

        <plugin>

          <artifactId>maven-clean-plugin</artifactId>

          <version>3.1.0</version>

        </plugin>

        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->

        <plugin>

          <artifactId>maven-resources-plugin</artifactId>

          <version>3.0.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-compiler-plugin</artifactId>

          <version>3.8.0</version>

        </plugin>

        <plugin>

          <artifactId>maven-surefire-plugin</artifactId>

          <version>2.22.1</version>

        </plugin>

        <plugin>

          <artifactId>maven-war-plugin</artifactId>

          <version>3.2.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-install-plugin</artifactId>

          <version>2.5.2</version>

        </plugin>

        <plugin>

          <artifactId>maven-deploy-plugin</artifactId>

          <version>2.8.2</version>

        </plugin>

      </plugins>

    </pluginManagement>

  </build>

</project>



파일명: pom.xml


[첨부(Attachments)]

pom.zip



6. Servlet 생성하기


서블렛 생성에 대한 것이다.



그림 14. Java Resources 폴더의 마우스 오른쪽 버튼 모습


Java Resources를 마우스 오른쪽 버튼으로 클릭한다.

New->Servlet을 클릭한다.



그림 15. Create Servlet


Java package를 입력해준다. (예: com.example.web.controller)

Class Name을 입력해준다. (예: FrontController)


Finish를 누른다.




7. Web.xml 수정하기


경로: /src/main/webapp/WEB-INF/web.xml


<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns="http://java.sun.com/xml/ns/javaee" 

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" 

id="WebApp_ID" version="3.0">

  <display-name>Archetype Created Web Application</display-name>

  <welcome-file-list>

  <welcome-file>index.jsp</welcome-file>

  </welcome-file-list>

  <servlet>

  <servlet-name>FrontController</servlet-name>

  <servlet-class>com.example.web.controller.FrontController</servlet-class>

  <init-param>

<param-name>charset</param-name>

<param-value>UTF-8</param-value>

</init-param>

  </servlet>

  <servlet-mapping>

  <servlet-name>FrontController</servlet-name>

  <url-pattern>*.do</url-pattern>

  </servlet-mapping>

</web-app>


파일명: web.xml


[첨부(Attachments)]

web.zip



8. Controller - Interface 생성하기


경로: /src/main/java/com/example/web/controller/Controller.java


package com.example.web.controller;


import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


public interface Controller {


public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException;


}




파일명: Controller.java


[첨부(Attachments)]

Controller.zip



9. Controller - board/InsertController.java


경로: /src/main/java/com/example/web/controller/board/InsertController.java


package com.example.web.controller.board;


import java.io.IOException;


import javax.servlet.ServletException;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;


import com.example.web.controller.Controller;

import com.example.web.util.HttpUtil;


public class InsertController implements Controller {


@Override

public void execute(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {

HttpUtil.forward(req, res, "/WEB-INF/views/board/result.jsp");


}


}


파일명: InsertController.java


[첨부(Attachments)]

InsertController.zip




10. Util - util/HttpUtil.java (업로드 개선 및 문자열 구분 처리)


토큰, 업로드 개선, 문자열 구분 처리 등을 추가적으로 담아보았다.


경로: /src/main/java/com/example/web/util/HttpUtil.java


package com.example.web.util;


import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;


import javax.servlet.RequestDispatcher;

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.disk.DiskFileItemFactory;

import org.apache.commons.fileupload.servlet.ServletFileUpload;


public class HttpUtil extends HttpServlet {


private static final long serialVersionUID = 1L;


private static String charset = null; // 문자열


    private static final int MEMORY_THRESHOLD   = 1024 * 1024 * 3;  // 3MB // 

    private static final long MAX_FILE_SIZE      = 1024 * 1024 * 40; // 40MB

    private static final long MAX_REQUEST_SIZE   = 1024 * 1024 * 50; // 50MB

 

private static String UPLOAD_FOLDER = "upload";

private static String UPLOAD_TMP_FOLDER = File.separator + "WEB-INF" + File.separator + "temp";

private static List<String> reqInfoList = null; // req 정보(MultiRequest) - 문맥 방식

private static List<Object> fileInfoList = null; // 다중 파일 지원 - 문맥 방식

private static Map<String, Object> reqInfoMap = null; // req 정보(MultiRequest) - Map 자료구조

private static Map<Integer, Map<String, Object>> fileInfoMap = null; // 다중 파일 지원 - Map 자료구조

private static boolean FILE_TOKEN_CHECK = false; // req 토큰 체크하기 

private static String FILE_AUTH_TOKEN_KEY = null; // req 인증키 추가

private static String FILE_RESTRICT_EXT = ".jpg.jpeg.bmp.png.gif.txt"; // 파일 확장자 제한

private static int reqNum = 0; // reqNum

private static int fileNum = 0; // fileNum


public static void forward(HttpServletRequest req, HttpServletResponse res,

String path) throws ServletException, IOException {


try {

RequestDispatcher dispatcher = req.getRequestDispatcher(path);

dispatcher.forward(req, res);


}catch(Exception e) {

e.printStackTrace();

}

}

public static void uploadFile(HttpServletRequest req, HttpServletResponse res) throws

ServletException, IOException {


reqInfoList = new ArrayList<String>();

fileInfoList = new ArrayList<Object>();


fileInfoMap = new HashMap<Integer, Map<String, Object>>(); 

reqInfoMap = new HashMap<>();

// 파일 인증키 인증 완료 후 업로드

List<FileItem> tmpItems = new ArrayList<FileItem>();

FILE_AUTH_TOKEN_KEY = "1";

reqNum = 1;

fileNum = 1;

    

    PrintWriter out = res.getWriter();

    //out.println("<HTML><HEAD><TITLE>Multipart Test</TITLE></HEAD><BODY>");

try {

        

        //디스크상의 프로젝트 실제 경로얻기

        //String contextRootPath = "c:" + File.separator + "upload";

String dirName = UPLOAD_FOLDER ; 

// String dirName = "upload"; 

String contextRootPath = req.getSession().getServletContext().getRealPath("/") + dirName;

        System.out.println("실제경로:" + contextRootPath);

        

        //1. 메모리나 파일로 업로드 파일 보관하는 FileItem의 Factory 설정

        DiskFileItemFactory diskFactory = new DiskFileItemFactory(); // 디스크 파일 아이템 공장

        diskFactory.setSizeThreshold(MEMORY_THRESHOLD); // 업로드시 사용할 임시 메모리

        // diskFactory.setSizeThreshold(4096); // 업로드시 사용할 임시 메모리

        diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

        // diskFactory.setRepository(new File(contextRootPath + UPLOAD_TMP_FOLDER)); // 임시저장폴더

        

        //2. 업로드 요청을 처리하는 ServletFileUpload생성

        ServletFileUpload upload = new ServletFileUpload(diskFactory);

        // upload.setSizeMax(3 * 1024 * 1024); //3MB : 전체 최대 업로드 파일 크기

        upload.setSizeMax(MAX_REQUEST_SIZE);

        

        // sets maximum size of upload file(파일 단위 업로드 크기)

        upload.setFileSizeMax(MAX_FILE_SIZE);

        

        //3. 업로드 요청파싱해서 FileItem 목록구함​​

        List<FileItem> items = upload.parseRequest(req); 

        Iterator<FileItem> iter = items.iterator(); //반복자(Iterator)로 받기​            

        while(iter.hasNext()) { //반목문으로 처리​    

            FileItem item = (FileItem) iter.next(); //아이템 얻기

             //4. FileItem이 폼 입력 항목인지 여부에 따라 알맞은 처리

            

            if(item.isFormField()){ 

            //파일이 아닌경우

                processFormField(out, item);

                

            } else {

            //파일인 경우

            // System.out.println("오류:" + item.getName());

           

            // 버그 개선 item 이름값 비어있을 때

            if ( item.getName() != "") {

            tmpItems.add(item);

            // processUploadFile(out, item, contextRootPath);

            }

            // System.out.println("오류2:");

            }

        }

        

        // 파일 토큰 확인될 경우

        if ( FILE_TOKEN_CHECK ) {

       

        // 파일 업로드 처리

        for (FileItem readyItem: tmpItems)

        {

        processUploadFile(out, readyItem, contextRootPath);

        }

       

        }

        

        

        

    } catch(Exception e) {

        e.printStackTrace(out);

    }

//out.println( "usrID(Map): " + reqMap.get("usrID") );

//out.println( "usrPasswd(Map):" + reqMap.get("usrPasswd") );

    

    // out.println("</BODY></HTML>");

// req.setAttribute("usrID", reqMap.get("usrID"));

// req.setAttribute("login", 1);//Object Type으로 넘어감

    req.setAttribute("reqInfoMap", reqInfoMap);

    req.setAttribute("fileInfoMap", fileInfoMap);

    req.setAttribute("reqInfoList", reqInfoList);

    req.setAttribute("fileInfoList", fileInfoList);

    

// System.out.println("오류3:" + reqMap.get("usrID"));

    

}

//업로드한 정보가 파일인경우 처리

private static void processUploadFile(PrintWriter out, FileItem item, String contextRootPath)

throws Exception {

Map<String, Object> fileMapNode = new HashMap<String, Object>();

List<String> fileListNode = new ArrayList<String>();

boolean resultUpload = false;

String dirName = UPLOAD_FOLDER ; 

String name = item.getFieldName(); // 파일의 필드 이름 얻기

String fileName = item.getName(); // 파일명 얻기

// 임시 - 실제 원본 이름 추출

File originalFile = new File(fileName);

String originalFileName = originalFile.getName();

System.out.println("임시:" + originalFileName );

String contentType = item.getContentType(); // 컨텐츠 타입 얻기

long fileSize = item.getSize(); // 파일의 크기 얻기

// 업로드 파일명을 현재시간으로 변경후 저장

String fileExt = fileName.substring(fileName.lastIndexOf("."));

String uploadedFileName = System.currentTimeMillis() + ""; 

System.out.println(fileExt);

System.out.println(uploadedFileName);

// 저장할 절대 경로로 파일 객체 생성

String realUploadFile = File.separator + dirName + File.separator + uploadedFileName;

System.out.println("실제 저장직전폴더:" + contextRootPath + realUploadFile);

File uploadedFile = new File(contextRootPath + realUploadFile);

if ( FILE_RESTRICT_EXT.contains(fileExt.toLowerCase()) ) {

item.write(uploadedFile); // 파일 저장

resultUpload = true; // 파일 업로드 완료

}

//========== 뷰단에 출력 =========//

//out.println("<P>");

//out.println("파라미터 이름:" + name + "<BR>");

//out.println("파일 이름:" + fileName + "<BR>");

//out.println("콘텐츠 타입:" + contentType + "<BR>");

//out.println("파일 사이즈:" + fileSize + "<BR>");

//확장자가 이미지인겨우 이미지 출력

/*

if(".jpg.jpeg.bmp.png.gif".contains(fileExt.toLowerCase())) {

out.println("<IMG SRC='upload/" 

+ uploadedFileName 

+ "' width='300'><BR>");

}

*/

// out.println("</P>");

// out.println("<HR>");

// out.println("실제저장경로 : "+uploadedFile.getPath()+"<BR>");

// out.println("<HR>");

// 파일 전송이 완료되었을 때

if ( resultUpload == true ) {

// 파일 정보(Map 자료구조 방식)

fileMapNode.put("name", name);

fileMapNode.put("fileName", originalFileName);

fileMapNode.put("contentType", contentType);

fileMapNode.put("fileSize", fileSize);

fileMapNode.put("fileExt", fileExt);

fileMapNode.put("uploadedFileName", uploadedFileName);

fileMapNode.put("realName", uploadedFile.getName());

fileMapNode.put("realPath", uploadedFile.getPath());

// 파일 정보(문자열 구분 방식)

fileListNode.add( "id:" + fileNum + ",name:name,value:" + name + "/" );

fileListNode.add( "id:" + fileNum + ",name:filename,value:" + originalFileName + "/" );

fileListNode.add( "id:" + fileNum + ",name:contentType,value:" + contentType + "/" );

fileListNode.add( "id:" + fileNum + ",name:fileSize,value:" + fileSize + "/" );

fileListNode.add( "id:" + fileNum + ",name:fileExt,value:" + fileExt + "/" );

fileListNode.add( "id:" + fileNum + ",name:uploadedFileName,value:" + uploadedFileName + "/" );

fileListNode.add( "id:" + fileNum + ",name:realName,value:" + uploadedFile.getName() + "/" );

fileListNode.add( "id:" + fileNum + ",name:realPath,value:" + uploadedFile.getPath() + "/" );

fileInfoList.add(fileListNode);

fileNum++;

}

}

private static void processFormField(PrintWriter out, FileItem item) 

throws Exception{

String name = item.getFieldName(); //필드명 얻기

Object value = item.getString("UTF-8"); //UTF-8형식으로 필드에 대한 값읽기

//out.println(name + ":" + value + "<BR>"); //출력

// 파일 토큰 인증 여부

if ( name.contentEquals("token")

&& value.equals(FILE_AUTH_TOKEN_KEY) ) {

System.out.println("참 - 인증");

FILE_TOKEN_CHECK = true;

}

// Map 자료구조 저장

reqInfoMap.put(name, value);

// 문자열 구문 저장 - 문자열 검색원리 적용 (검색 기법 적용)

reqInfoList.add( "id:" + reqNum + ",name:" + name + ",value:" + value + "/" );

reqNum++;

}

// FileInfoList 내용 - 문맥 문석기(임시)

public static void getFileinfoParser(List<Object> fileParser, String tarName) {

// 분석 시작

if ( fileParser != null ) {

for ( Object fileNode : fileParser ) {

@SuppressWarnings("unchecked")

List<String> sep = (List<String>) fileNode;

// 문자열 분석 반환 받기 (id = 0, name = 1, value = 2)

Object result = getFileAttrAnal(sep, tarName);

}

}

}


// FileInfoList 내용 - 문자열 구분

private static Object getFileAttrAnal(List<String> fileNode, String tarName) {

int pId = -1;

String pName = null;

Object pValue = null;

String trashTxt = null;

if ( fileNode != null) {

for ( String usrTxt : fileNode  ) {

trashTxt = usrTxt.substring(usrTxt.indexOf("id:") + 3, usrTxt.indexOf(",name") );

pId = Integer.valueOf(trashTxt);

// System.out.print("ID:" + pId);


trashTxt = usrTxt.substring(usrTxt.indexOf("name:") + 5, usrTxt.indexOf(",value") );

pName = trashTxt;

// System.out.print(",name:" + pName);

trashTxt = usrTxt.substring(usrTxt.indexOf("value:") + 6, usrTxt.indexOf("/") );

pValue = trashTxt;

// System.out.println(",value:" + pValue);

}

}

return null;

}


// ReqInfoList 내용 - 문맥 문석기(임시)

public static Object getReqinfoParser(List<String> reqNode, String tarName) {

int pId;

String pName ;

Object pValue ;

String trashTxt;

if ( reqNode != null) {

for ( String usrTxt : reqNode  ) {

trashTxt = usrTxt.substring(usrTxt.indexOf("id:") + 3, usrTxt.indexOf(",name") );

pId = Integer.valueOf(trashTxt);

System.out.print("ID:" + pId);


trashTxt = usrTxt.substring(usrTxt.indexOf("name:") + 5, usrTxt.indexOf(",value") );

pName = trashTxt;

System.out.print(",name:" + pName);

trashTxt = usrTxt.substring(usrTxt.indexOf("value:") + 6, usrTxt.indexOf("/") );

pValue = trashTxt;

System.out.println(",value:" + pValue);

}

}

return null;

}

}


파일명: HttpUtil.java


[첨부(Attachments)]

HttpUtil.zip




11. Model - CompUsers.java


모델은 DB의 테이블 설계를 생각해서 하는 것이 좋다고 본다. (꼭 그렇다는 건 아님.)


아래의 그림 16, 그림 17, 그림 18은 결국은 같은 Table에 대한 것이다.

하나를 바라보는 것이 다양할 수 있다는 이야기를 하는 것이다.



그림 16. Oracle SQL Developer - COMP_USERS (Tables)



그림 17. Oracle SQL Developer - COMP_USERS (Tables)



그림 18. Oracle SQL Developer - COMP_USERS (Tables)


비고: 이 글에서는 DB 설계는 하지 않았음.



경로: /src/main/java/com/example/web/model/CompUsers.java


package com.example.web.model;


public class CompUsers {


private String username;

private String password;

private int enabled;

public CompUsers(String username, String password, int enabled) {

this.username = username;

this.password = password;

this.enabled = enabled;

}


public CompUsers() {

}


public String getUsername() {

return username;

}

public void setUsername(String username) {

this.username = username;

}

public String getPassword() {

return password;

}

public void setPassword(String password) {

this.password = password;

}

public int getEnabled() {

return enabled;

}

public void setEnabled(int enabled) {

this.enabled = enabled;

}

}



파일명: CompUsers.java


[첨부(Attachments)]

CompUsers.zip



* 2부에서는 Jquery 다운받기, JSON, MultiUploadController, View 화면, Index 페이지 구성에 대해서 소개하겠다.


1. [JSP] 25. jQuery와 Ajax 멀티 파일 업로드(개선), POST전송, JSON - 구성하기(2), 2020-10-03

https://yyman.tistory.com/1446


반응형

+ Recent posts