spring mvc 스프링 바코드 생성 하기 Zxing 사용법

안녕하세요. spring 프로젝트중 바코드를 생성해서 회원에게 날려줘야 할일이 생겼어요. 

그래서 지금 바코드 생성 관련해서 찾아보니 zxing를 사용한 방법이 공개되어 있었어요. 

일단 메이븐에서 라이브러리 다운로드를 위해 설정을 합니다. 

		<!-- https://mvnrepository.com/artifact/com.google.zxing/core -->
		<!-- 구글바코드 오픈소스 -->
		<dependency>
		    <groupId>com.google.zxing</groupId>
		    <artifactId>core</artifactId>
		    <version>3.4.0</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/com.google.zxing/javase -->
		<dependency>
		    <groupId>com.google.zxing</groupId>
		    <artifactId>javase</artifactId>
		    <version>3.4.0</version>
		</dependency>

그리고나서 컨트롤러 위치한 곳에 바코드 이미지를 받아가는 코딩을 합니다. 

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;

라이브러리를 import를 하고요. 

@RequestMapping(value = "/get_barcode", method = RequestMethod.GET)
	public ResponseEntity<byte[]> get_partner_image(HttpServletRequest request, Model model) throws Exception {
		 smsp.print_String("get_barcode ==>" + "아악."  );
		String text ="https://digital365365.com";
        String _formatName = text;
        text = new String(text.getBytes("UTF-8"), "ISO-8859-1");
      
 		byte[] imageInByte_re = smsp.getBarCodeImage(text, 840, 160);
 		//smsp.print_String(image.toString());
 		HttpHeaders headers = new HttpHeaders(); 
 		String mt_filename = text+".png";
        String formatName = "png";	      
	      MediaType mType = MediaUtils.getMediaType(formatName);
	      //smsp.print_String(mType.toString());
	      if(mType != null){
		        headers.setContentType(mType);
		      }else{
		    	mt_filename = new String(mt_filename.getBytes("UTF-8"), "ISO-8859-1");
		        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
		        headers.add("Content-Disposition", "attachment;filename=\""+mt_filename+"\";");
		        headers.add("Content-Transfer-Encoding", "binary");
		      }
       
	      return new ResponseEntity<byte[]>(imageInByte_re, headers,HttpStatus.OK);
	}

바코드 생성코드는 이렇게 있습니다. 

public static byte[] getBarCodeImage(String text, int width, int height) {
		try {
			Hashtable<EncodeHintType, ErrorCorrectionLevel> hintMap = new Hashtable<>();
			hintMap.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
			Writer writer = new Code128Writer();
			BitMatrix bitMatrix = writer.encode(text, BarcodeFormat.CODE_128, width, height);
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			MatrixToImageWriter.writeToStream(bitMatrix, "png", byteArrayOutputStream);
			return byteArrayOutputStream.toByteArray();
		} catch (Exception e) {
			return null;
		}
	}

이상 정리해보았습니다. 

<img src="${pageContext.request.contextPath}/cp_a/get_barcode" alt="바코드">

 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

SPRING MVC 세팅 설정 시작하기!

SPRING BOOT도 좋지만 저는 SPRING MVC로 프로젝트를 진행을 하는 편이에요.

환경설정 등을 기존에 해놓은 것을 그대로 사용할 수 있어 그런데요.

일단 STS를 실행하고 새로운 작업공간을 설정해요. 

새로운작업공간

전 DENTALAPP이라는 폴더를 작업공간으로 설정했어요. 

스프링 레거시 프로젝트

오른쪽 마우스를 누르고 New -> Spring Legacy Project를 선택해요.

패키지

그럼 새로운 작업 패키지명을 적는데요 저는 com에 제 이름의 약어인 sms와 2019를 붙이고 home이라고 적었어요. 

앱이름

프로젝트 이름을 dentalapp이라고 하였고요.  템플릿은 Spring MVC Project를 선택하였어요. 

컨트롤러

홈컨트롤러 파일과 홈 JSP파일이 생기는데요. 

JSP파일

JSP파일은 페이지를 UTP-8로 사용하겠다는 내용을 추가해줘요. 안 그러면 한글이 깨져서 나와요. 

그럼 프로젝트를 실행해볼까요?

저는 톰캣 8로 설정하고 돌렸어요. 

결과물

결과가 잘 나오고 있네요. 한글도 안 깨지고요. 

앞으로 쭉 프로젝트를 진행 볼게요. 모바일 웹앱을 만들 생각이고요. 안드로이드랑 아이폰으로 패키징을 할 계획이에요. 

여기까지 정리할게요. 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

Spring java excel download 스프링 엑셀 다운 poi

SRPING MVC 이미지

안녕하세요. 웹 개발일을 하다 보면 엑셀 파일을 요청받을 일들이 있어요. 

SPRING에서는 아파치 소프트웨어 재단에서 만든 POI를 사용할 수 있어요. 

마이크로소프트 오피스 파일들을 읽고 쓰는 기능을 제공하는데요. 워드, 엑셀, 파워포인트 파일을 지원하고 있어요. 최근 OFFICE OPEN XML FILE FORMATS(*. DOCX,*. XLSX,*. PPTX)등 지원 파일을 늘려가고 있다고 해요. 

처음 메이븐 설정을 해요. 

		<!-- 엑셀파일 -->
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi</artifactId>
		    <version>3.13</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.poi</groupId>
		    <artifactId>poi-ooxml</artifactId>
		    <version>3.13</version>
		</dependency>
		<dependency>
		    <groupId>org.apache.commons</groupId>
		    <artifactId>commons-collections4</artifactId>
		    <version>4.0</version>
		</dependency>

SERVLET-CONTEXT.XML 파일 내용을 찾아보면 InternalResourceViewResolver가 선언되어 있을거에요. 

<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

JSP페이지로 이름을 매핑해주는것인데 대부분 설정이 되어 있을 거예요.

저희가 받아야 할 건 JSP페이지가 아니에요. 

XmlViewResolver를 설정해줘야 해요. 조금 귀찮긴 한데 한번 만들어 놓으면 다른 프로젝트에도 복사해서 사용하면 돼요.

<beans:bean id="viewResolver1" class="org.springframework.web.servlet.view.XmlViewResolver">
        <beans:property name="order" value="1"/>
        <beans:property name="location" value="/WEB-INF/views.xml"/>
    </beans:bean>

views.xml파일위치

views.xml파일을 만들어요. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   	<bean id="mr001_exceldownload_View" class="com.sms2019.excel.mr001_exceldownload_View" />
</beans>

views.xml
0.00MB

그리고 소스파일에 views.properties 추가를 해요. 

views.properties

mr001_exceldownload_View.(class)=com.sms2019.excel.mr001_exceldownload_View

views.properties
0.00MB

컨트롤러 부분에 요청받는 부분을 만들어요. 

@RequestMapping(value = "/g402_excel_download", method = RequestMethod.GET)
  public ModelAndView g402_excel_download(HttpServletRequest request, Model model) throws Exception {
	  	Util_list_convert smsp=Util_list_convert.getInstance();
	  	smsp.print_String("/gp/g402_excel_download");
		Map<String, String[]> paramMap=request.getParameterMap();
		paramMap.forEach(
				(key, value) ->  smsp.print_String(key + " : " + value[0])
				);
		Iterator keyData = paramMap.keySet().iterator();
		CommonData dto = new CommonData();	
		paramMap.forEach(
				(key, value) -> {dto.put(key, value[0]);
				smsp.print_String(key + " : " + value[0]);
				model.addAttribute(key,value[0].toString());
				});
	    CommonData data_box = new CommonData();
	    List Title_list = new ArrayList();    
	    Title_list.add("번호");
	    Title_list.add("품목명");
	    Title_list.add("규격");
	    Title_list.add("파트");
	    Title_list.add("수량");
	    Title_list.add("팀");

	    List Title_list_coll = new ArrayList();
	    Title_list_coll.add("rownum");
	    Title_list_coll.add("item_name");
	    Title_list_coll.add("item_standard");
	    Title_list_coll.add("part_division_text");
	    Title_list_coll.add("quantity");
	    Title_list_coll.add("team_code_text");
	    
	    dto.put("exceldown","exceldown");
	    List<Map<String, Object>> list = z_service.select(dto,"Gp2018_Mapper.g402_list_select");
	    ModelAndView mav = new ModelAndView("mr001_exceldownload_View");
	    mav.addObject("Title_list", Title_list);
	    mav.addObject("Title_list_coll", Title_list_coll);
	    mav.addObject("list", list);
	    mav.addObject("Filename", "주문목록");
	    return mav;
  }

엑셀 파일 데이터를 만들어요 db에서 읽어와도 좋고요 엑셀 윗부분을 요청 시 받아와도 되고요 전 컨트롤러를 각각 만들어서 써요. 그래서 타이틀이랑 항목 칼럼 이름 그리고 데이터 그리고 파일 이름 넣어요. 

그럼 MadelAndView에 적힌 부분으로 호출이 되는데요.

package com.danawa.excel;

import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.view.document.AbstractExcelView;

import com.danawa.domain.CommonData;
import com.danawa.util.Util_list_convert;

@Controller
public class mr001_exceldownload_View extends AbstractExcelView {
 
    @Override
    protected void buildExcelDocument(Map<String, Object> model,
            HSSFWorkbook workbook, HttpServletRequest request, HttpServletResponse response)
            throws Exception {
    	Util_list_convert smsp=Util_list_convert.getInstance();
	  	smsp.print_String("Common_exceldownload");
    	String Filename = (String)model.get("Filename");
		String fileName = Filename+".xls";
		fileName = URLEncoder.encode(fileName,"UTF-8");
        response.setContentType("application/ms-excel; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition","attachment; filename="+fileName);
        // get data model which is passed by the Spring container
        List<String> Title_list = (List<String>) model.get("Title_list");
        List<String> Title_list_coll = (List<String>) model.get("Title_list_coll");
    	List<CommonData> list = (List<CommonData>) model.get("list");
        // create a new Excel sheet
        HSSFSheet sheet = workbook.createSheet("sheet1");
        sheet.setDefaultColumnWidth(30);
         
        // create style for header cells
        CellStyle style = workbook.createCellStyle();
        CellStyle centerstyle = workbook.createCellStyle();
        CellStyle centerbluestyle = workbook.createCellStyle();
       
        Font font = workbook.createFont();
        font.setFontName("Arial");        
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        font.setColor(HSSFColor.WHITE.index);
       // style.setFillForegroundColor(HSSFColor.BLACK.index);
       // style.setFillPattern(CellStyle.SOLID_FOREGROUND);
        style.setBorderBottom (HSSFCellStyle.BORDER_THIN);
        style.setBottomBorderColor (HSSFColor.BLACK.index);
        style.setBorderLeft (HSSFCellStyle.BORDER_THIN);
        style.setLeftBorderColor (HSSFColor.BLACK.index);
        style.setBorderRight (HSSFCellStyle.BORDER_THIN);
        style.setRightBorderColor (HSSFColor.BLACK.index);
        style.setBorderTop (HSSFCellStyle.BORDER_THIN);
        style.setTopBorderColor (HSSFColor.BLACK.index);
        //style.setFont(font);        
        //centerstyle.setFillForegroundColor(HSSFColor.WHITE.index);
        //centerstyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
        centerstyle.setAlignment(CellStyle.ALIGN_CENTER);
        centerstyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        centerstyle.setBottomBorderColor(HSSFColor.BLACK.index);
        centerstyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        centerstyle.setLeftBorderColor(HSSFColor.BLACK.index);
        centerstyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
        centerstyle.setRightBorderColor(HSSFColor.BLACK.index);
        centerstyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
        centerstyle.setTopBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setFillForegroundColor(HSSFColor.BLACK.index);
        centerbluestyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
        centerbluestyle.setAlignment(CellStyle.ALIGN_CENTER);
        centerbluestyle.setFillBackgroundColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setBottomBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setLeftBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setRightBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setTopBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setFont(font);    
         
        // create header row
        HSSFRow header = sheet.createRow(0);
        
        int cellCount;
        cellCount=0;
        int columns_length=0;
        //제목 값 세팅 한다. 
        for(String item : Title_list){
        	header.createCell(cellCount).setCellValue(item);
            header.getCell(cellCount).setCellStyle(centerbluestyle);
            cellCount++;
            columns_length++;
    	}
        
        // create data rows
        int rowCount = 1;   
        String sitem_name = null;
        String item_standard = null;
        String sitem_name_replace=null;
        Iterator list_object = list.iterator();	    
        while (list_object.hasNext()) {
        	CommonData e_object = (CommonData) list_object.next();
            HSSFRow aRow = sheet.createRow(rowCount++);
            cellCount=0; 
            
            for(String item : Title_list_coll){
                String value = e_object.get(item);
                smsp.print_String("item : " + value);
                aRow.createCell(cellCount).setCellValue(value);
	            aRow.getCell(cellCount).setCellStyle(centerstyle);            
	            cellCount++;
            }
    	}
        for(int ni=1;ni<=sheet.getLastRowNum();ni++)
        { 
        	sheet.getRow(ni).setHeightInPoints((float)20);
        	 
        }
       
        autoSizeColumns(sheet,sheet.getRow(0).getLastCellNum());
    }
    private void autoSizeColumns(HSSFSheet sheetData, int maxColNum) {
   	 try {
   		 // Autosize columns
   		 int width = 0;
   		 for (int col = 0; col < maxColNum; col++) {
   			 sheetData.autoSizeColumn(col);
   			 int cwidth = sheetData.getColumnWidth(col);
   			 cwidth += 500;
   			 sheetData.setColumnWidth(col, cwidth);
   			 width += cwidth;
   		 }

   		 // calculate zoom factor
   		 int nominator = 45000 * 100 / width;
   		 if (nominator < 100)
   			 sheetData.setZoom(nominator, 100);

   	 } catch (Exception he) {
   		 // No UI, no autosize :(
   	 }
    }
 
}

이렇게 엑셀 내용을 채워 엑셀 파일이 만들어져요.

String Filename = (String)model.get("Filename");
		String fileName = Filename+".xls";
		fileName = URLEncoder.encode(fileName,"UTF-8");
        response.setContentType("application/ms-excel; charset=UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-Disposition","attachment; filename="+fileName);

파일 이름을 설정하고 utf8로 다운로드돼요.

List<String> Title_list = (List<String>) model.get("Title_list");
        List<String> Title_list_coll = (List<String>) model.get("Title_list_coll");
    	List<CommonData> list = (List<CommonData>) model.get("list");

엑셀 파일의 항목 타이틀과 각 항목의 column의 이름 그리고 데이터 받아와요. 

HSSFSheet sheet = workbook.createSheet("sheet1"); 
        sheet.setDefaultColumnWidth(30);

sheet를 만들어요. 만들 때 이름을 정해줄 수 있어요. 전 sheet1이라고 했어요. 

// create style for header cells
        CellStyle style = workbook.createCellStyle();
        CellStyle centerstyle = workbook.createCellStyle();
        CellStyle centerbluestyle = workbook.createCellStyle();
       

cell 스타일을 미리 만들었어요.  단순해요. style는 흰색의 배경에 검은색의 테두리 선을 가진 것이고요. centerstyle는 중앙 정렬한 흰색 배경에 검은색 테두리 구요. centerbluestyle는 중앙 정렬에 파란 배경을 과 검은색 테두리를 가진 스타일이에요. 

style.setBorderBottom (HSSFCellStyle.BORDER_THIN);
        style.setBottomBorderColor (HSSFColor.BLACK.index);
        style.setBorderLeft (HSSFCellStyle.BORDER_THIN);
        style.setLeftBorderColor (HSSFColor.BLACK.index);
        style.setBorderRight (HSSFCellStyle.BORDER_THIN);
        style.setRightBorderColor (HSSFColor.BLACK.index);
        style.setBorderTop (HSSFCellStyle.BORDER_THIN);
        style.setTopBorderColor (HSSFColor.BLACK.index);        
        centerstyle.setAlignment(CellStyle.ALIGN_CENTER);
        centerstyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        centerstyle.setBottomBorderColor(HSSFColor.BLACK.index);
        centerstyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        centerstyle.setLeftBorderColor(HSSFColor.BLACK.index);
        centerstyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
        centerstyle.setRightBorderColor(HSSFColor.BLACK.index);
        centerstyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
        centerstyle.setTopBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setFillForegroundColor(HSSFColor.BLACK.index);
        centerbluestyle.setFillPattern(CellStyle.SOLID_FOREGROUND);
        centerbluestyle.setAlignment(CellStyle.ALIGN_CENTER);
        centerbluestyle.setFillBackgroundColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderBottom(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setBottomBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setLeftBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderRight(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setRightBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setBorderTop(HSSFCellStyle.BORDER_THIN);
        centerbluestyle.setTopBorderColor(HSSFColor.BLACK.index);
        centerbluestyle.setFont(font);    

직관적으로 만들어놓아서 대충 알아보실 거 같아요. 소스량을 줄이려면 함수를 만들면 되겠죠? 그건 직접 해보세요 ^^

 Font font = workbook.createFont();
        font.setFontName("Arial");        
        font.setBoldweight(HSSFFont.BOLDWEIGHT_BOLD);
        font.setColor(HSSFColor.WHITE.index);

폰트를 따로 선언해서 사용해야 할 일이 있을 경우엔 폰트도 추가해주세요 전 타이틀 부분은 폰트를 다르게 했어요 흰색으로요. ^^

HSSFRow header = sheet.createRow(0);

첫 번째 로우를 만들고요 

 int cellCount;
        cellCount=0;
        int columns_length=0;
        //제목 값 세팅 한다. 
        for(String item : Title_list){
        	header.createCell(cellCount).setCellValue(item);
            header.getCell(cellCount).setCellStyle(centerbluestyle);
            cellCount++;
            columns_length++;
    	}

첫번째 행에 항목 타이틀 값들을 넣어줘요... 

 // create data rows
        int rowCount = 1;   
        String sitem_name = null;
        String item_standard = null;
        String sitem_name_replace=null;
        Iterator list_object = list.iterator();	    
        while (list_object.hasNext()) {
        	CommonData e_object = (CommonData) list_object.next();
            HSSFRow aRow = sheet.createRow(rowCount++);
            cellCount=0; 
            
            for(String item : Title_list_coll){
                String value = e_object.get(item);
                smsp.print_String("item : " + value);
                aRow.createCell(cellCount).setCellValue(value);
	            aRow.getCell(cellCount).setCellStyle(centerstyle);            
	            cellCount++;
            }
    	}

컨트롤러에서 넘겼던 칼럼 항목 이름을 순으로 위치로 해서 셀에 넣어요. 

이상으로 엑셀 파일 다운로드 관련해서 정리해보았습니다. 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

dropzone 사용법 SPRING MVC 다중파일 멀티파일

웹프로그램 제작시 파일을 여러개 올리고 받아야할 경우가 발생해요.

이경우 dropzone라는 라이브러리를 사용하면 편리하게 구현할수 있어요. 

https://www.dropzonejs.com/

 

Dropzone.js

dropzone.js DropzoneJS is an open source library that provides drag’n’drop file uploads with image previews. It’s lightweight, doesn’t depend on any other library (like jQuery) and is highly customizable. Scroll down!

www.dropzonejs.com

사이트로 이동해서 라이브러리 파일을 다운로드 받아요. 

저는 게시판에 dropzone를 달았어요. 

<table class="table table-write" id="add_mt">
					<colgroup>			
					<col style="width:120px" />
					<col style="width:*" />											
					</colgroup>
					<tr>						
						<th>날짜</th>
						<td>
							<div class="input-group date form_date col-md-5" data-date="" data-date-format="yyyy-MM-dd" data-link-field="dtp_input2" data-link-format="yyyy-MM-dd">
				                    <input class="form-control" size="16" type="text" id ="mt_wdate" name="mt_wdate" value="${mt_wdate}" style="text-align:left;">                   
									<span class="input-group-addon" style="padding:0 10px;border-top:1px solid #ccc;border-right:1px solid #ccc;border-bottom:1px solid #ccc;">
										<span class="glyphicon glyphicon-calendar"></span>
									</span>
				              </div>
						</td>
					</tr>
					<tr>						
						<th>제목</th>
						<td><input type="text" class="form-control" id=mt_subject name="mt_subject" placeholder="제목" value="${mt_subject}" required></td>
					</tr>
					<tr>
						<th>내용</th>
						<td>
							<div id="summernote">${mt_content}</div>
						</td>
					</tr>
					<tr>
						<th>파일첨부</th>
						<td>
							<div id="my_file_upload" class="dropzone">
								<div class="fallback">
									<input name="file" type="file" multiple />
								</div>
							</div>
						</td>
					</tr>
				</table>

summernote와 같이 사용하고 있었는데요. 

dropzone.css와 dropzone.js를 선언해요.

<link href="../../resources/sms/dropzone/dropzone.css" rel="stylesheet">
<script src="../../resources/sms/dropzone/dropzone.js"></script>

그리고 $(document).ready(function() 로 dropzone에 대한 설정을 해요. 

Dropzone.autoDiscover = false;
$(document).ready(function() {	
	edit_mode_note();
	//$("div#my_file_upload").dropzone({ url: "/editor/file_upload" });
	$(".file-dropzone").on('dragover', handleDragEnter);
	$(".file-dropzone").on('dragleave', handleDragLeave);
	$(".file-dropzone").on('drop', handleDragLeave);

	function handleDragEnter(e) {
		this.classList.add('drag-over');
	}

	function handleDragLeave(e) {
		this.classList.remove('drag-over');
	}

	Dropzone.autoDiscover = false;
	
	$("div#my_file_upload").dropzone({ url: "/editor/file_uploads" ,
		addRemoveLinks : true,
			maxFilesize : 256,
		  dictResponseError: 'Error uploading file!',
		  headers: {
		    'X-CSRF-TOKEN': $("#csrf_token").val()
		    , uid : $('#i_uid').val()
		  },
		  params: {
		         uid: $('#i_uid').val()
		    },
		  uploadMultiple : true,
		  thumbnailWidth: 180,
		  thumbnailHeight: 120,
		  parallelUploads: 100,		
		  maxFiles: 255,
		  autoProcessQueue : true,
		  accept: function(file, done) {
			    if (file.name == "") {
			      done("제한사항 없어도 될듯 한뎅?");
			    }
			    else {			    	
			    	done(); 
			    	}
			  },
	init : function() {
		var myDropzone = this;
		var count = myDropzone.options.maxFiles;
		var url = "/editor/file_list";
		var data = JSON.stringify({ 
			uid : $('#i_uid').val()
		});
		
		var callback_init=function (data) {
            if (data.data != '' && data.data != undefined) {
            	var existingFileCount = 0;
                $.each(data.data, function (index, item) {
                    var mockFile = {
                        name: item.mt_filename,
                        size: item.mt_contentlength,
                        seq: item.idx
                    };
                    var filePath = "/editor/get_editor_thumbnail_image/?idx="+item.idx;
                    myDropzone.emit("addedfile", mockFile);
                    myDropzone.emit("thumbnail", mockFile, filePath);
                    myDropzone.emit("success", mockFile);
                    myDropzone.emit("complete", mockFile);                    
                    myDropzone.files.push(mockFile);
                    //file.previewElement.classList.add("dz-processing"); //sms;
                    //file.previewElement.classList.add("dz-complete");
                    existingFileCount = existingFileCount + 1;
                });
                myDropzone.options.maxFiles = myDropzone.options.maxFiles - existingFileCount;
            }
        };
		getPostData(url,data,callback_init);
		
		this.on("maxfilesexceeded", function (file) {
			showmessage("알림","한번에 올릴수 있는 파일의 갯수 제한을 넘었습니다",2000,'');
			myDropzone.removeFile(file);
		});
        
        this.on("success", function( file, resp, formData ){
        	existingFileCount=0;
        	var type = $("#"+id).data("type");
        	if(resp != null && resp != '' && resp != undefined) {
	        	if(resp.data != null && resp.data != '' && resp.data != undefined) {
	    			var existingFileCount = 0;
	    			$.each(resp.data, function(){
	    				if(this.mt_filename==file.name)
	    				{
	    					file.previewElement.appendChild(Dropzone.createElement("<input type='hidden' name='fileId' value='" + this.fileId + "'/>"));
	    					existingFileCount = existingFileCount + 1;
	    				}
	    			});
	    		}
        	}
        	if(file.seq != null && file.seq != '' && file.seq != undefined) {
				file.previewElement.appendChild(Dropzone.createElement("<input type='hidden' name='fileId' value='" + file.seq + "'/>"));
    		}
        });
        
        this.on("removedfile", function (data) {
        	var id = $(data.previewElement).find('input[name=fileId]').val();
    		var sql_state_value = 'delete';
    		var data = JSON.stringify({
    			   sql_state : sql_state_value		 
    			 , idx : id    	
    			 , uid : $('#i_uid').val()
    		   	 , mt_input_id : $('#login_mt_id').val()
    			 , mt_update_id : $('#login_mt_id').val()
    		});
    		var url ='/editor/editor_note_delete_img';
    		getPostData(url,data,null);
    		myDropzone.options.maxFiles = myDropzone.options.maxFiles + 1;
    		if(count < myDropzone.options.maxFiles) myDropzone.options.maxFiles = count;
    	    });
		}
	
	});
	$('.push').click(function() {
		$('.dropzone').each(function() {
			alert("어휴.");
		});
	});


});

headers: {
    'X-CSRF-TOKEN': $("#csrf_token").val()
    , uid : $('#i_uid').val()
  },

스프링부트 사용시 csrf-token을 headers에 넣어주시면 되요.

스프링쪽 컨트롤러는 /file_uploads는 이렇게 만들었어요. 

//멀티파일..
  @ResponseBody
  @RequestMapping(value ="/file_uploads", method=RequestMethod.POST, 
                  produces = "application/json;charset=UTF-8")
  public  Map<String, List<Map<String, String>>> file_uploads(MultipartFile nfile,MultipartHttpServletRequest  request)throws Exception{
	
	String uid=null;
	Map<String, String[]> paramMap1=request.getParameterMap();
	 Iterator keyData1 = paramMap1.keySet().iterator();	
    while (keyData1.hasNext()) {
        String key = ((String)keyData1.next());
        String[] value = paramMap1.get(key);
        logger.info("key : " + key + ", value : " + value[0].toString());
        if(key.equals("uid"))
        {
        	uid=value[0].toString().trim();
        }
    }    
	Map <String, MultipartFile > paramMap = request.getFileMap ();
	 Iterator keyData = paramMap.keySet().iterator();
	 CommonData dto = new CommonData();
	 
	 Map<String, List<Map<String, String>>> result = new HashMap<>();
	 List<Map<String, String>> data_list = new ArrayList();
	 
    while (keyData.hasNext()) {
    	Map<String, String> data = new HashMap<>();
        String key = ((String)keyData.next());
        MultipartFile file = paramMap.get(key);
        
        file_upload_ex(dto, data, file,uid);  //return ResponseEntity.ok().body(result);
        data_list.add(data);
        dto.clear();
    }
    result.put("data", data_list);
    return result;
  }

file_upload_ex부분은 이렇게 만들었어요. 

private String file_upload_ex(CommonData dto, Map<String, String> result, MultipartFile file, String uid)
		throws IOException, Exception {
	logger.info("originalName: " + file.getOriginalFilename());
    MediaUtils MediaUtils = new MediaUtils();
    String formatName = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf(".")+1);
    MediaType mType = MediaUtils.getMediaType(formatName);
    if(mType!=null)
    {
	    UploadFileUtils f_util = new UploadFileUtils();
	    BufferedImage  descimg = f_util.cropImageSquare(file.getBytes());
	    BufferedImage srcImg = ImageIO.read(file.getInputStream());
	    BufferedImage destImg = 
	            Scalr.resize(srcImg, 
	            		Scalr.Method.BALANCED, 
	                180,180
	                ,Scalr.OP_ANTIALIAS);
	    ByteArrayOutputStream baos = new ByteArrayOutputStream();
	    boolean foundWriter = ImageIO.write(destImg, "png", baos);
		baos.flush();
		byte[] imageInByte = baos.toByteArray();
		dto.put("mt_s_data", imageInByte);
		baos.close();
    }
	
	    dto.put("mt_filename",file.getOriginalFilename());
	    dto.put("mt_type",file.getContentType());
	    dto.put("mt_contentlength",file.getBytes().length);
	    dto.put("mt_data", file.getBytes());
	    dto.put("uid", uid);
	    
	
	    z_service.insert(dto, "File_UpDown_Mapper.insert_editor_image_upload");    
	    int idx= z_service.listSearchCount(dto, "File_UpDown_Mapper.select_editor_image_upload");
		result.put("result", "UPDATE_OK");
	    String url = "/editor/get_editor_image/?idx="+idx;
	    String id = ""+idx;
	    result.put("url", url);
	    result.put("id", id);	    
	    result.put("fileId", id);
	    result.put("mt_filename", file.getOriginalFilename());
	    result.put("mt_type", file.getContentType());
	    result.put("formatName", formatName);	 
	    result.put("uid", uid);	    
	    return id;
}

/file_list 쪽 컨트롤러 에요. 

@RequestMapping("/file_list")
public Map<String, List<Map<String, Object>>> file_list(@RequestBody CommonData dto) throws Exception {
	  Iterator keyData = dto.keySet().iterator();
      while (keyData.hasNext()) {
          String key = ((String)keyData.next());
          String value = dto.get(key);          
          logger.info("key : " + key + ", value : " + value);
      }
      List<Map<String, Object>> list = z_service.select(dto, "File_UpDown_Mapper.get_editor_image_uid_list");
      Map<String, List<Map<String, Object>>> result = new HashMap<>();
      result.put("data", list);
      return result;
  
}

/get_editor_thumbnail_image 컨트롤러 에요. 

@RequestMapping(value = "/get_editor_thumbnail_image", method = RequestMethod.GET)
  public ResponseEntity<byte[]> get_thumbnail_image(HttpServletRequest request, Model model) throws Exception {
	  	 MediaUtils MediaUtils = new MediaUtils();
		 Map<String, String[]> paramMap=request.getParameterMap();		 
		 Iterator keyData = paramMap.keySet().iterator();
		 CommonData dto = new CommonData();
	     while (keyData.hasNext()) {
	         String key = ((String)keyData.next());
	         String[] value = paramMap.get(key);
	         dto.put(key, value[0].toString());        
	         logger.info("key : " + key + ", value : " + value[0].toString());
	     }
	     List<Map<String, Object>> result_list;
	     result_list=z_service.select(dto,"File_UpDown_Mapper.get_editor_thumbnail_image");
	     /*
	     if(result_list.size() < 1)
	     {
	    	 dto.put("idx", "108");
	    	 result_list=z_service.select(dto,"File_UpDown_Mapper.get_editor_image");
	     }
	     */
	     logger.info(""+result_list.size());
	     if(result_list.size() > 0)
	     {
	    	  Iterator list_object = result_list.iterator();
	          Map<String, Object> e_object = (Map<String, Object>) list_object.next();
	          
	          String idx = e_object.get("idx").toString();
	          String mt_filename = (String)e_object.get("mt_filename");
	          byte[] mt_data = (byte[])e_object.get("mt_s_data");	
	          HttpHeaders headers = new HttpHeaders(); 
	          mt_filename = URLEncoder.encode(mt_filename,"UTF-8");
  	        //response.setContentType("application/ms-excel; charset=UTF-8");
  	        //response.setCharacterEncoding("UTF-8");
  	       // response.setHeader("Content-Disposition","attachment; filename="+mt_filename);
	          String formatName = mt_filename.substring(mt_filename.lastIndexOf(".")+1);	      
		      MediaType mType = MediaUtils.getMediaType(formatName);
		      if(mType != null){
			        headers.setContentType(mType);
			      }else{
			    	   String icon_idx= "1";
			    	   formatName = formatName.toUpperCase();
			    	   if(formatName.equals("TXT"))
			    	   {
			    		   icon_idx = "9";
			    	   }
			    	   else if(formatName.equals("XLS")||formatName.equals("XLSX")||formatName.equals("XLSM")||formatName.equals("XLSB")||formatName.equals("XLTX")||formatName.equals("XLTM"))
			    	   {
			    		   icon_idx = "2";
			    	   }
			    	   else if(formatName.equals("MP3"))
			    	   {
			    		   icon_idx = "3";
			    	   }
			    	   else if(formatName.equals("MP4"))
			    	   {
			    		   icon_idx = "4";
			    	   }
			    	   else if(formatName.equals("PDF"))
			    	   {
			    		   icon_idx = "5";
			    	   }
			    	   else if(formatName.equals("PPT")||formatName.equals("PPTX"))
			    	   {
			    		   icon_idx = "6";
			    	   }
			    	   else if(formatName.equals("XML"))
			    	   {
			    		   icon_idx = "7";
			    	   }
			    	   else if(formatName.equals("ZIP"))
			    	   {
			    		   icon_idx = "8";
			    	   }
			    	   else if(formatName.equals("DOC"))
			    	   {
			    		   icon_idx = "10";
			    	   }
			    	   dto.put("idx", icon_idx);
			    	   result_list=z_service.select(dto,"File_UpDown_Mapper.get_editor_thumbnail_image");
			    	   if(result_list.size() > 0)
				  	   {
				  	    	  list_object = result_list.iterator();
				  	          e_object = (Map<String, Object>) list_object.next();
				  	          
				  	          idx = e_object.get("idx").toString();
				  	          mt_filename = (String)e_object.get("mt_filename");
				  	          mt_data = (byte[])e_object.get("mt_s_data");	
				  	          headers = new HttpHeaders(); 
				  	          formatName = mt_filename.substring(mt_filename.lastIndexOf(".")+1);	      
				  		      mType = MediaUtils.getMediaType(formatName);
				  		      if(mType != null){
				  		    	  headers.setContentType(mType);
				  		      }
				  	   }
			      }
	         
		      return new ResponseEntity<byte[]>(mt_data, headers,HttpStatus.OK);
	      }
	     return new ResponseEntity<byte[]>(HttpStatus.BAD_REQUEST);
  	}

저는 위에 등록된파일들을 썸네일 아이콘으로 등록해뒀어요. 파일종류에 따라서 아이콘을 다르게 보이게 할려구요.

/editor_note_delete_img 파일 삭제 컨트롤러 에요.

  @RequestMapping("/editor_note_delete_img")
  public Map<String, String> editor_note_delete_img(@RequestBody CommonData dto) throws Exception {
	  Map<String, String> paramMap = new HashMap<>();
	  
	  Iterator keyData = dto.keySet().iterator();
      while (keyData.hasNext()) {
          String key = ((String)keyData.next());
          String value = dto.get(key);          
          logger.info("key : " + key + ", value : " + value);
      }
      z_service.update(dto, "File_UpDown_Mapper.editor_note_delete_img");
      paramMap.put("result", "IMAGE_DELETE_OK");     
      return paramMap;
  }

파일다운로드는 게시판을 읽어올때 받아와요. 

<div class="view">
				<input type="hidden" class="form-control" id="i_idx" value="${idx}">
				<input type="hidden" class="form-control" id="i_uid" value="${uid}">
				<input type="hidden" class="form-control" id="mt_class" value="${mt_class}">
				<br style="clear: both">
				<!-- 
				<h3 style="margin-bottom: 25px;">뭘 이렇게 까지 자료가.. @@</h3>
				-->
				<div id="mt_subject" class="title">
					${mt_subject}
					<!-- input type="text" class="form-control" id=mt_subject name="mt_subject" placeholder="제목" value="${mt_subject}" required-->
				</div>
				<div id="date" class="date">
					${mt_wdate}
				</div>
				<div id="summernote" class="contents">
					${mt_content}
					<!--  div id="summernote">${mt_content}</div-->
				</div>
               <!-- 파일정보에요. -->
				<div id="mt_file_list" class="filt_list">
					<p class="attachment">첨부파일 다운로드</p>
							
					<c:forEach items="${file_list}" var="file_list"  varStatus="status">
						<a href="/editor/get_editor_image/?idx=${file_list.idx}" download="${file_list.mt_filename}">
						<img src="/editor/get_editor_thumbnail_image/?idx=${file_list.idx}"/>
						${file_list.mt_filename}
						</a>						
					</c:forEach>						
				</div>				  
			</div>

첨부파일 다운로드에서 파일리스트를 가져와서 뿌려주고 아이콘을 클릭하면 다운로드 되요. 

@RequestMapping(value = "/get_editor_image", method = RequestMethod.GET)
  public ResponseEntity<byte[]> get_image(HttpServletRequest request, Model model) throws Exception {
	  	 MediaUtils MediaUtils = new MediaUtils();
		 Map<String, String[]> paramMap=request.getParameterMap();		 
		 Iterator keyData = paramMap.keySet().iterator();
		 CommonData dto = new CommonData();
	     while (keyData.hasNext()) {
	         String key = ((String)keyData.next());
	         String[] value = paramMap.get(key);
	         dto.put(key, value[0].toString());        
	         logger.info("key : " + key + ", value : " + value[0].toString());
	     }
	     List<Map<String, Object>> result_list;
	     result_list=z_service.select(dto,"File_UpDown_Mapper.get_editor_image");
	    
	     logger.info(""+result_list.size());
	     if(result_list.size() > 0)
	     {
	    	  Iterator list_object = result_list.iterator();
	          Map<String, Object> e_object = (Map<String, Object>) list_object.next();
	          
	          String idx = e_object.get("idx").toString();
	          String mt_filename = (String)e_object.get("mt_filename");
	          mt_filename = URLEncoder.encode(mt_filename,"UTF-8");
	          byte[] mt_data = (byte[])e_object.get("mt_data");	
	          HttpHeaders headers = new HttpHeaders(); 
	          String formatName = mt_filename.substring(mt_filename.lastIndexOf(".")+1);	      
		      MediaType mType = MediaUtils.getMediaType(formatName);
		      if(mType != null){
			        headers.setContentType(mType);
			      }else{
			    	mt_filename = new String(mt_filename.getBytes("UTF-8"), "ISO-8859-1");
			        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
			        headers.add("Content-Disposition", "attachment;filename=\""+mt_filename+"\";");
			        headers.add("Content-Transfer-Encoding", "binary");
			      }
	         
		      return new ResponseEntity<byte[]>(mt_data, headers,HttpStatus.OK);
	      }
	     return new ResponseEntity<byte[]>(HttpStatus.BAD_REQUEST);
  	}
  

이상으로 dropzone.js를 사용하여 파일을 업로드 삭제 하고 다운로드 하는 것을 해보았어요.

 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

스프링 에러java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

컴퓨터 사용계정을 몇일전에 기본으로 변경했어요.

그랬더니 프로젝트 실행할려니 여러문제를 겪게 되었는데요. 

기존 작성한 프로젝트를 열었어요. 메이븐을 업데이트 했어요.

Maven  -> Update Project

프로젝트를 실행하니 에러가 발생합니다. 

java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener

구글검색을 하니까 문제 해결법이 나왔어요. 

Deployment Assembly

프로젝트 설정에서 Deployment Assembly에 Maven Dependencies를 추가해야 합니다. 

add버턴을 눌러주세요.

NEW Assembly Directive

java build path entries를 눌러주세요. 

NEW Assembly Directive

Maven Dependencies 선택하고 Finish 를 눌러주세요. 

Deployment Assembly

Maven Dependencies 가 추가된 것을 확인할수 있습니다. 

닫고 다시 실행을 해보세요. 

저는 추가로 

Lambda expressions are allowed only at source level 1.8 or above

이게 뜨더라구요. 아.. 자바 버전 1.8로 설정했어요. 

Java Compiler

저장하고 실행하니 다시 작동했어요. 

참고 사이트는 아래 사이트입니다. 

https://jaeu0608.tistory.com/155

 

[Error] Eclipse에서 Tomcat 실행시 에러 Error configuring application listener of class org.springframework.web.context.ContextLoaderLis

Eclipse에서 Tomcat 구동 에러 ( maven 사용시 ) 심각: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener java.lang.ClassNotFoundException: org.spring..

jaeu0608.tistory.com

수고하세요.

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

홈페이지를 구축하다보면 기존 도메인과 개발도메인 로컬 도메인등 여러 주소를 사용할 경우가 있습니다.

저의 경우 홈페이지 구매후 신규 개발을 들어 갔는데 이름역시 디지털아트에서 디지털365치과의원으로 변경되어 

홈페이지를 신규개발한후 365에 맞는 도메인 https://digital365365.com/ 를 구매하여 설정하였고

기존에 운영중인던 도메인 dadentalclinic.com 도메인과 디지털365치과.com 등을 추가로 설정하였습니다.

그런데 블로그나 사이트 등록시 https://digital365365.com/ 로 하면 좋았을텐데 여러가지 도메인들을 사용하게 되었습니다.

그래서 도메인을 임의로 통합시키는 작업을 해주었습니다. 

<beans:bean id="metaInterceptor" class="com.sms2019.interceptor.MetaInterceptor"></beans:bean>

인터셉터 를 하나 만들어 줍니다. 

인터셉터 내용은 아래와 같습니다.

String m_url = request.getRequestURL().toString();
		smsp.print_String("m_url :" + m_url);
		if(m_url.contains("검사주소 넣기"))
		{
			smsp.print_String("바른주소");
		}
		else if(m_url.contains("http://localhost"))
		{
			smsp.print_String("로콜주소");
		}
		else
		{
			smsp.print_String("그외 주소다.");
			String new_addr = "https://digital365365.com"+originalURL;
			response.sendRedirect(new_addr);
		}

이상입니다. 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

범일동 수치과 홈페이지를 새로 작성하고 있었습니다. 

http://soo-dent.com/

 

부산 범일동 수치과의원

부산 범일동 치과 임플란트 틀니 사랑니 충치치료 신경치료 보철치료 심미치료 스케일링, 부산임플란트, 서면임플란트, 지르코니아

soo-dent.com

인스타 연동이 안되어 있습니다. 인스타를 사진을 가져와 볼려고 했습니다.

기존 인스타그램 가져오는 api가 지원이 중단되었습니다. 

https://www.instagram.com/developer/

 

Instagram Developer Documentation

 

www.instagram.com

인스타그램 중단문구

 Instagram Basic Display API뭔가 싶어 들어가서 따라해보니 잘 안되어 있네요.  

가이드문서가 정리가 잘 안되어 있어서 구글검색을해보니 크롤링해서 사진을 가져오는 분들이 보였습니다.

저도 크롤링으로 가져오기로 합니다. 

일단 크롤링으로 데이터를 가져와보니 인스타내용이 보이지 않았습니다.

인스타는 자바스크립트소스를 보내주고 사용자환경에서 스크립트를 실행하여 데이터를 받아오는 형태입니다.. 

셀레니움으로 하는 방법이 있던데 일반 사람이 접속해서 스크립트가 실행된후 브라우저 데이터값을 가져올수 있었습니다.

일종의 자동화 도구인데 자바역시 지원하여 정리 합니다. 

셀레니움을 받아 설치를 합니다. 

https://www.seleniumhq.org/ 에서 윈도우 버전의 셀레니움을 받습니다. 

<!-- 크롬 셀리니움 -->
		
		<dependency>
	        <groupId>org.seleniumhq.selenium</groupId>
	        <artifactId>selenium-java</artifactId>
	        <version>3.141.59</version>
	    </dependency>

pom.xml 에 추가합니다. 

   //WebDriver
	    private WebDriver driver;
	    private WebElement element;
	    //Properties
	    public static final String WEB_DRIVER_ID = "webdriver.chrome.driver";
	    public static final String WEB_DRIVER_PATH = "chromedriver.exe";

WEB_DRIVER_PATH 를 잘설정합니다.

 System.setProperty(WEB_DRIVER_ID, WEB_DRIVER_PATH);
	        driver = new ChromeDriver();
	        base_url = "https://www.instagram.com/soodental9/";
   driver.get(base_url);

이러면 인스타그램 페이지 내용을 가져옵니다. 

 Document doc = Jsoup.parse(driver.getPageSource());
	            // HTML 문서의 타이틀 추출하기
	    		System.out.println("HTML TITLE : " + doc.title());

가져온내용을 JSOUP 를이용하여 파싱을 합니다. 

크롬디버그모드로 본 소스

소스에서 가져와야될 부분을 찾습니다. 

Elements tables = doc.select(".ySN3v"); //클래스..
	    		Elements linksOnPage = tables.select(".v1Nh3.kIKUG._bz0w"); //클래스..

데이터를 가져와서 출력해보았습니다. 

int i=1;
				 for (Element page : linksOnPage) {
					System.out.println("count : "+i++);
					Element link = page.select("a").first();
					String linkHref = link.attr("href"); 
					System.out.println("a href : "+base_url+linkHref);
					Element img = page.select("img").first();
					String imgtag = img.outerHtml();
					System.out.println("img : "+imgtag);
				 }

인스타 그램 내용.

테스트 페이지를 만들어서 위내용을 붙여 넣어 봅니다. 

이미지가 잘 보이네요.

img 태그와  a태그를 db에 넣어서 불러와서 사용하면 되겠습니다. 내용갱신은 하루에 한번 하도록 하면 되겠네요.

아래는 소스 전체 내용입니다. 

package com.sms2019.da;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

public class instagram_Selenium {
	
	 public static void main(String[] args) {
		 
	        instagram_Selenium selTest = new instagram_Selenium();
	        selTest.crawl();
	    }
	 
	    
	    //WebDriver
	    private WebDriver driver;
	    private WebElement element;
	    //Properties
	    public static final String WEB_DRIVER_ID = "webdriver.chrome.driver";
	    public static final String WEB_DRIVER_PATH = "chromedriver.exe";
	    
	    //크롤링 할 URL
	    private String base_url;
	    public instagram_Selenium() {
	        super();
	        //System Property SetUp
	        System.setProperty(WEB_DRIVER_ID, WEB_DRIVER_PATH);
	        driver = new ChromeDriver();
	        base_url = "https://www.instagram.com/soodental9/";
	    }
	 
	    public void crawl() {
	        try {
	            //get page (= 브라우저에서 url을 주소창에 넣은 후 request 한 것과 같다)
	        	int waittime=10;
	            driver.get(base_url);
	            JavascriptExecutor js = (JavascriptExecutor) driver;
	          
	            //System.out.println(driver.getPageSource());
	            Document doc = Jsoup.parse(driver.getPageSource());
	            // HTML 문서의 타이틀 추출하기
	    		System.out.println("HTML TITLE : " + doc.title());
	    		Elements tables = doc.select(".ySN3v"); //클래스..
	    		Elements linksOnPage = tables.select(".v1Nh3.kIKUG._bz0w"); //클래스..
	    		int i=1;
				 for (Element page : linksOnPage) {
					System.out.println("count : "+i++);
					Element link = page.select("a").first();
					String linkHref = link.attr("href"); 
					System.out.println("a href : "+base_url+linkHref);
					Element img = page.select("img").first();
					String imgtag = img.outerHtml();
					System.out.println("img : "+imgtag);
				 }
				 System.out.println("======================================================");
	    
	        } catch (Exception e) {
	            
	            e.printStackTrace();
	        
	        } finally {
	            driver.close();
	        }
	 
	    }
}
블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

이클립스 흰색 에디터 배경이 눈이 아픈경우가 있다. 

이럴때 다크테마를 사용하면 편리하다. 

이클립스 상단 메뉴중 Window -> Preferences 누른다.

메뉴

다음 General -> Appearance -> Theme

메뉴중 dark를 누르면 색상이 변경된다. 

 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

웹프로그램을 만들다보면 그리드 시스템이 필요할떄가 있습니다. 

테이블태그를 이용한 코드 작성도 나쁘진 않지만 JQGRID를 사용해보고 개발의 편리성에 GRID라이브러리를 사용하게 되었습니다. 

JQGRID를 사용해 보았으니 최신버전은 유료라이센스로 바뀌었고 4.0대 버전을 무료로 사용할수 있었습니다. 

대체할 라이브러리를 검색을 하다 발견한 JSGRID를 사용을 해보게 되었습니다. 

JQGRID라이브러리의 예제 소스는 아래에서 다운받거나 구경할수 있습니다.

http://js-grid.com/demos/

 

Demos

 

js-grid.com

저는 JSGRID 라이브러리를 스프링에 적용하는 것을 정리합니다. 

라이브러리 로드 파일에 라이브러리경로를 넣어줍니다. 

    <link rel="stylesheet" type="text/css" href="../css/jsgrid.css" />
    <link rel="stylesheet" type="text/css" href="../css/theme.css" />
    
    <script src="../src/jsgrid.core.js"></script>    
    <script src="../src/jsgrid.load-indicator.js"></script>
    <script src="../src/jsgrid.load-strategies.js"></script>
    <script src="../src/jsgrid.sort-strategies.js"></script>
    <script src="../src/jsgrid.validation.js"></script>
    <script src="../src/jsgrid.field.js"></script>
    <script src="../src/fields/jsgrid.field.text.js"></script>
    <script src="../src/fields/jsgrid.field.number.js"></script>
    <script src="../src/fields/jsgrid.field.select.js"></script>
    <script src="../src/fields/jsgrid.field.checkbox.js"></script>
    <script src="../src/fields/jsgrid.field.control.js"></script>

각자의 경로에 맞추어 넣어주면 됩니다. 

jsgrid를 선언하는 부분은 이런식으로 나와 있습니다. 

 $("#jsGrid").jsGrid({
                height: "70%",
                width: "100%",
                filtering: true,
                editing: true,
                inserting: true,
                sorting: true,
                paging: true,
                autoload: true,
                pageSize: 15,
                pageButtonCount: 5,
                deleteConfirm: "Do you really want to delete the client?",
                controller: db,
                fields: [
                    { name: "Name", type: "text", width: 150, validate: "required" },
                    { name: "Age", type: "number", width: 50, validate: { validator: "range", param: [18,80] } },
                    { name: "Address", type: "text", width: 200, validate: { validator: "rangeLength", param: [10, 250] } },
                    { name: "Country", type: "select", items: db.countries, valueField: "Id", textField: "Name",
                        validate: { message: "Country should be specified", validator: function(value) { return value > 0; } } },
                    { name: "Married", type: "checkbox", title: "Is Married", sorting: false },
                    { type: "control" }
                ]
            });

나누어 보겠습니다. 

위부분을 인수로 받아 생성하는 코드로 변경을 합니다.

//그리드 생성.
function initGrid(grid_id,in_fields,in_paging,in_pageSize,in_pageButtonCount,in_filtering,in_inserting,in_editing,in_sorting,in_autoload
		,now_pager,now_table_custom,now_db
		,onrowClass,inonItemInserting,inonItemUpdating)
{
	jsGrid.locale("ko");
	grid_id.jsGrid({
		
		noDataContent: "데이터가 없습니다.",
		deleteConfirm: "삭제합니다 정말입니까?",
		pagerFormat: " {prev} {pages} {next}",
		pagePrevText: "&laquo;",
		pageNextText: "&raquo;",
		pageFirstText: "처음",
		pageLastText: "마지막",
		loadMessage: "잠시 기다려주십시오 .",
		invalidMessage: "입력 된 데이터가 잘못되었습니다.",
		
        height: "auto",
        width: "100%",
        filtering: in_filtering,
        editing: in_editing,
        inserting: in_inserting,
        sorting: in_sorting,
        paging: in_paging,
        pageLoading: true,
        autoload: in_autoload,
        pageSize: in_pageSize,
        pageButtonCount: in_pageButtonCount,
        controller: now_db,
        fields: in_fields,
        rowClass: onrowClass,
        onItemInserting: inonItemInserting,
        onItemUpdating: inonItemUpdating,
        onItemInserted:function(){grid_id.jsGrid("loadData");},
    });
	now_table_custom.on("click", function() {
        var $cb = $(this);
        grid_id.jsGrid("option", $cb.attr("id"), $cb.is(":checked"));
    });
    now_pager.on("change", function() {
        var page = parseInt($(this).val(), 10);
        grid_id.jsGrid("option", "pageSize", page); 
        grid_id.jsGrid("refresh");
    });
}

jsgrid로  한페이지에 다수의 그리드를 생성 관리할수 있게 되었습니다. 

<div id="jsGrid"></div> //기본 예제 그리드 id

입력되는 값들을 기본 디폴드 값으로 분리한 선언부를 만들어 놓습니다. 

//조각 조각 입력해서 그리드 생성하기..
var sms_js_size = [];
sms_js_size["width"]="100%";
sms_js_size["height"]="auto";
var sms_basic_setting= [];
sms_basic_setting["autoload"]=true; //화면 로딩시 데이터를 불러옴.
sms_basic_setting["confirmDeleting"]=true; //삭제시 정말 지울건지 물어봄. 
sms_basic_setting["pagerContainer"] = null; //페이지 이동 위치 .. 
sms_basic_setting["pageIndex"] = 1; //시작페이지 위치.. 기본 1페이지..
sms_basic_setting["pageSize"] = 15; //화면에 나타날 기본 줄수... 15개를 기본으로한다. 
sms_basic_setting["loadIndication"] = true; // 제어기 조작 실행 중로드 표시를 표시할지 여부를 지정하는 부울 값.
sms_basic_setting["loadIndicationDelay"]= 500; //로드 표시를 표시하기 전의 지연을 ms 단위로 지정하는 정수 값
sms_basic_setting["loadShading"] = true; //표시를로드하는 동안 격자 내용 위에 오버레이 (셰이더)를 표시할지 여부를 지정하는 부울 값?
sms_basic_setting["updateOnResize"] = true;//윈도우 크기 변경 이벤트에서 격자를 새로 고칠 지 여부를 지정하는 부울 값
var sms_in_ting = [];
sms_in_ting["heading"]= true; //격자 머리글 표시 여부를 지정합니다.
sms_in_ting["filtering"]= true; // 필터 행 표시 여부를 지정
sms_in_ting["inserting"]= true; // 행 삽입 여부를 지정합니다.
sms_in_ting["editing"]= true; //편집이 허용되는지 여부를 지정합니다.
sms_in_ting["selecting"]= true; //마우스를 올리면 표 행을 강조 표시할지 여부를 지정합니다.
sms_in_ting["sorting"]= true; // 정렬 허용 여부를 지정합니다.
sms_in_ting["paging"]= true; // 페이지별로 데이터를 표시할지 여부
sms_in_ting["pageLoading"]= true; //부울 값은 페이지별로 데이터를로드할지 여부를 지정
var sms_in_row_function = [];
sms_in_row_function["rowClass"]=function (item, itemIndex)  {};
sms_in_row_function["rowClick"]=function ( args ) {};
sms_in_row_function["rowDoubleClick"]=function ( args ) {};
sms_in_row_function["invalidNotify"]=function ( args ) {};
var sms_in_callback = [];
sms_in_callback["onDataLoading"]=function ( args ) {};
sms_in_callback["onDataLoaded"]=function ( args ) {};
sms_in_callback["onError"]=function ( args ) {};
sms_in_callback["onInit"]=function ( args ) {};
sms_in_callback["onItemInserting"]=function ( args ) {};
sms_in_callback["onItemInserted"]=function ( args ) {};
sms_in_callback["onItemUpdating"]=function ( args ) {};
sms_in_callback["onItemUpdated"]=function ( args ) {};
sms_in_callback["onItemDeleting"]=function ( args ) {};
sms_in_callback["onItemDeleted"]=function ( args ) {};
sms_in_callback["onItemInvalid"]=function ( args ) {};
sms_in_callback["onOptionChanging"]=function ( args ) {};
sms_in_callback["onOptionChanged"]=function ( args ) {};
sms_in_callback["onPageChanged"]=function ( args ) {};
sms_in_callback["onRefreshing"]=function ( args ) {};
sms_in_callback["onRefreshed"]=function ( args ) {};
function get_JsSize()
{
	var return_obj = {};
	//copyArray(return_obj,sms_js_size);
	for (var key in sms_js_size )
	{
		return_obj[key]=sms_js_size[key];
	}
	return return_obj;
}
function get_JsBasic_setting()
{
	var return_obj = {};
	//copyArray(return_obj,sms_basic_setting);
	for (var key in sms_basic_setting )
	{
		return_obj[key]=sms_basic_setting[key];
	}
	return return_obj;
}
function get_JsIn_tinge()
{
	var return_obj = {};
	//copyArray(return_obj,sms_in_ting);
	for (var key in sms_in_ting )
	{
		return_obj[key]=sms_in_ting[key];
	}
	return return_obj;;
}
function get_JsIn_row_function()
{
	var return_obj = {};
	//copyArray(return_obj,sms_in_row_function);
	for (var key in sms_in_row_function )
	{
		return_obj[key]=sms_in_row_function[key];
	}
	return return_obj;;
}
function get_JsIn_callback()
{
	var return_obj = {};
	//copyArray(return_obj,sms_in_callback);
	for (var key in sms_in_callback )
	{
		return_obj[key]=sms_in_callback[key];
	}
	return return_obj;;
}

분리됨 모듈라이브러리파일을 jsgrid가 사용되는 페이지에서 로드하여 줍니다. 

페이지 부분에 들어가는 소스입니다.

<div id="jsGrid"></div>	  //그리드 위치 선언

자바스크립트 부분
$(document).ready(function(){	
     var db = {
    		loadData: function (filter) {
                return  $.ajax({
                    type: "POST",
                    headers : {
    					"Content-Type" : "application/json",
    					"X-HTTP-Method-Override" : "POST",
    					'X-CSRF-TOKEN': $("#csrf_token").val()
    				},
    				async: true,
                    url: getContextPath()+"/grid/select",
                    data: read_common_data("Appointment_Mapper.a2_list_all_select", filter),
                    error: function (request, status, error) {
                    	alert('code:' + request.status + '\n' + 'message:' + request.responseText + '\n' + 'error:' + error);
                    	}
                });
            }        
        };
    window.db = db;
    window.db.comcode = new Object();    
    window.db.comcode["mt_use"] = [         
    								{ Name: "선택", Id: "" },
						            { Name: "Y", Id: "Y" },
						            { Name: "N", Id: "N" }
						           ];

이제 그리드 등 연결해 줍니다.

now_grid=$("#jsGrid");
    now_pager= $("#pager");
    now_table_custom=$(".config-panel input[type=checkbox]");
    now_db = db;
    /* 달력 */ 
    jsGrid.fields.MySelectControlField = MySelectControlField;

필드값을 설정해줍니다.

 var fields =  [
                   { name: "idx", title:"번호", type:"number", width: 60 ,align: "center" , valueType: "number",readOnly: true},
                   { name: "mdate", title:"게시일", type: "text",  width: 100, align: "center" },
                   { name: "title", title:"제목", type: "text",  width: 300, align: "left" ,readOnly: true},
                   { name: "dname", title:"구분", type: "text",  width: 60, align: "left" ,readOnly: true},
                   { title:"답변상태",width: 100 ,align: "center",
                	   itemTemplate: function(_, item) {
                		   var parent = $("<p>");
                		   var $result_bt = $("<button>")
	   					    .addClass( "btn btn-blue btn_icon" )
                         	.on("click", function() {                             	
                             	
                         	});
               		        $result_bt.append( "답변완료" );
               		     	
               		     var $result_bt1=$(" <button>")
	   					    .addClass( "btn btn-lightblue btn_icon" )
                      	.on("click", function() {                             	
                          	    
                      	});
               		     $result_bt1.append( "미답변" );
               			if(item.answer=='Y')
               			{
               				parent.append($result_bt);	
               			}
               			else
               			{
               				parent.append($result_bt1);
               			}
               		
		       		  
		       		  return parent;		     
                	   }},
                	   {title:"답변메일",width: 100 ,align: "center",
                	   itemTemplate: function(_, item) {
                		   var parent = $("<p>");
                		   var $result_bt = $("<button>")
	   					    .addClass( "btn btn-blue btn_icon" )
                         	.on("click", function() {                             	
                         		email_send(item);
                         	});
               		        $result_bt.append( "보내기" );
               		            		        
               		  		if(item.memailsend=='Y')
	             			{
	             				parent.append("답장보냄");	
	             			}
	             			else
	             			{
	             				parent.append("답장안함");
	             			}
               		  		parent.append(item.memail);               		  	
							parent.append($result_bt);
		       		  return parent;		     
                	   }},                 	   
                   { type: "MySelectControlField", selectButton: true, deleteButton:false, modeSwitchButton: false, editButton: false, width: 120 },
                   { title:"공개상태",width: 100 ,align: "center",
                	   itemTemplate: function(_, item) {
                		   var parent = $("<p>");
                		   var $result_bt = $("<button>")
	   					    .addClass( "btn btn-blue btn_icon" )
                         	.on("click", function() {                             	
                             	
                         	});
               		        $result_bt.append( "공개" );
               		     	
               		     var $result_bt1=$(" <button>")
	   					    .addClass( "btn btn-lightblue btn_icon" )
                      	.on("click", function() {                             	
                          	    
                      	});
               		     $result_bt1.append( "미공개" );
               			if(item.always=='Y')
               			{
               				parent.append($result_bt);	
               			}
               			else
               			{
               				parent.append($result_bt1);
               			}
               		
		       		  
		       		  return parent;		     
                	   }},
                   { title:"공개삭제",width: 100 ,align: "center",
                	   itemTemplate: function(_, item) {
                		   var parent = $("<p>");
                		   var $result_bt = $("<button>")
	   					    .addClass( "btn btn-blue btn_icon" )
                         	.on("click", function() {                             	
                             	naver_send_w(item);
                         	});
               		        $result_bt.append( "공개" );
               		     	
               		     var $result_bt1=$(" <button>")
	   					    .addClass( "btn btn-lightblue btn_icon" )
                      	.on("click", function() {                             	
                          	    naver_send_d(item);
                      	});
               		     $result_bt1.append( "삭제" );
               			
		       		  parent.append($result_bt,'&nbsp;',$result_bt1);
		       		  return parent;		     
                	   }} 
               ];

다음은 인서트나 업데이트시 체크되야할부분과 업데이트인서트 기능을 설정해줍니다.

 var onItemInserting = function(args) {
        // cancel insertion of the item with empty 'name' field
    	f_values_chack(args);
    }
    var onItemUpdating = function(args) {
    	// cancel insertion of the item with empty 'name' field
    	f_values_chack(args);
    }
    var in_filtering=true;
    var in_editing=false;
    var insert_row=false;
    initGrid(now_grid,fields,true,15,10,in_filtering,insert_row,in_editing,true,true,now_pager,now_table_custom,now_db,OnrowClass,onItemInserting,onItemUpdating);

여기서 f_values_chack함수를 만들어줍니다. 

var f_values_chack = function(args)
{
	if(args.item.mt_code === "") {
    	jQuery.each( args.grid.fields, function( key, value ) //반복
    	{
    		if(value.name==="mt_code")
    		{
    			showmessage("경고",value.title+"을(를) 입력값이 누락되었습니다. 확인해 주세요.",2000,'error');
    		}
    	});
        args.cancel = true;
        return;
    }
    if(args.item.mt_code_value === "") {
    	jQuery.each( args.grid.fields, function( key, value ) //반복
     	{
     		if(value.name==="mt_code_value")
     		{
     			showmessage("경고",value.title+"을(를) 입력값이 누락되었습니다. 확인해 주세요.",2000,'error');
     		}
     	});     	
       	args.cancel = true;
       	return;
    }
}

실행해보면 나옵니다. 

JSGRID

 

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,

웹페이지에 상담신청이 왔을시 E-MAIL로 받게 했었다. 알람설정을 했지만 스마트폰을 잘보지 않아

문자로 보게 하는게 좋겠다고 하여 카페24에서 서비스 중이 SMS호스팅을 이용하기로 했다.

https://www.cafe24.com/

 

카페24

No.1 글로벌 전자상거래 플랫폼 '카페24'

www.cafe24.com

SMS서비스 및 비용

적당한 상품을 고르고 신청을 합니다. 

상품메뉴

메뉴가 있는데 발신번호 관리에서 번호를 인증해줍니다.

디지털365치과의원 번호로 인증함

디지털365치과의원 대표전화번호로 인증을 받았습니다.

restTemplate 이용해서 보내봅니다. 소스입니다.

	
    	String url = "https://sslsms.cafe24.com/sms_sender.php";
		String secret_key = "시크릿키";
		Util_Message smsp = Util_Message.getInstance(); //메세지 찍어보는거 
		CommonData result = new CommonData();
		smsp.print_String("/sms_send"); //위치sms_send 
		smsp.print_CommonData(dto); //db에서 읽어온 문자 메시지 내용 		
		RestTemplate restTemplate = new RestTemplate(); 
		HttpHeaders headers = new HttpHeaders();
	    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
	    LinkedMultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
	    map.add("user_id", "유저ID");	    
	    map.add("secure", secret_key); //시크릿키
	    map.add("msg", dto.get("content")); //SMS내용
	    map.add("sphone1", "052"); //인증된 발신번호
	    map.add("sphone2", "710");
	    map.add("sphone3", "5020");
	    map.add("rphone", dto.get("to_phone")); //수신 휴대번호
	    map.add("title", "디지털365치과의원"); //타이틀 ..
	    map.add("rdate", ""); //예약일자
	    map.add("rtime", "");  //예약시간
	    map.add("mode", "1");
	    //map.add("testflag", "Y");	 //테스트시..
	    HttpEntity<LinkedMultiValueMap<String, String>> request2 = new HttpEntity<LinkedMultiValueMap<String, String>>(map, headers);
        
	    String response2 = restTemplate.postForObject( url, request2, String.class );
	    smsp.print_String(response2);

테스트를 해보니 문자가 잘 받아집니다. 

문자메세지

블로그 이미지

은호아빠

여행, 맛집, 일상, 프로그래밍, 개발자, 윈도우, 웹, jsp, spring, db, mysql, oracle, c#

,