본문으로 건너뛰기

검색등록 - 2. Bing (Yahoo)

· 약 2분

Microsoft 검색인 Bing 과 Yahoo 는 한 곳에서 관리할 수 있다.

웹 마스터

Bing 웹 마스터 도구에 로그인한다. Microsoft 계정이 있으면 바로 로그인할 수 있다.

등록

사이트를 등록한다. image from hexo

인증

파일업로드 방식과 meta 태그 추가방식, CNAME 추가방식이 있는데 meta 태그 추가 방식을 사용한다. image from hexo 페이지에 태그를 추가하고 확인을 누르면 페이지 소유권이 확인되어 웹마스터 도구를 이용할 수 있다.

sitemap 등록

내 사이트 구성 > Sitemaps 메뉴에서 sitemap 을 제출한다. image from hexo 2-3 일 정도 지나야 요청이 처리되는 것 같다.

크롤링 시간대 설정

내 사이트 구성 > 크롤링 제어 메뉴로 이동해 크롤링 횟수를 제한하지말고 기본값으로 설정한다. image from hexo

여담

기능적으로 베타인 게 많아서 시간을 두고 인덱싱 되는 페이지를 확인해봐야 될 듯. 구글 다음으로 사용량이 많은게 Bing 과 Yahoo 검색이니 꼭 추가해주자.

바이두 검색등록 및 전화인증 없이 회원가입하기

· 약 4분

중국 1 위 검색사이트인 바이두에 내 사이트를 추가해보자. 바이두 사이트에서 회원가입을 하는 경우 인증번호를 입력해야하는데 국외번호 인증이 안된다.

회원가입

바이두 클라우드로 이동하여 회원가입 버튼을 클릭한다. image from hexo

국제번호를 변경 후 휴대번호로 인증한다. image from hexo

  • 전화번호
  • 아이디
  • 인증번호
  • 비밀번호

순으로 입력하면 된다.

인증하고 아래 큰 버튼을 누르면 가입과정이 완료된다.

웹 마스터

등록

바이두 웹마스터로 들어가 사이트 등록 버튼을 클릭한다. image from hexo

URL 을 입력하고 아래 버튼을 클릭한다. image from hexo

인증

파일업로드 방식과 meta 태그 추가 방식 중 후자를 선택했다. image from hexo 메타 태그를 헤더에 추가해주자.

단순한 링크 등록

블로그 등 파일을 올릴 수 없는 사이트의 링크만 등록하고 싶다면 여기서 URL 을 입력해준다. (Baidu 에서는 site 인증을 통한 방법을 권장하고 있다.)

sitemap 등록

sitemap 등록 메뉴로 이동합니다. image from hexo

경로 입력

textarea 에 사이트맵 경로를 라인마다 입력한다.

인증문자 입력

간체자는 한자키로 입력할 수 없는 문자가 많기에 네이버 중국어사전의 필기 인식기 기능을 사용한다. image from hexo 한글자씩 복사해 입력해준다.

제출

제출하면 아래 부분에 등록된 sitemap 이 보인다. image from hexo

Automatic Push

스크립트를 추가해 자동으로 페이지를 인덱싱할 수 있다.

소스

<script>
(function () {
var bp = document.createElement("script");
var curProtocol = window.location.protocol.split(":")[0];
if (curProtocol === "https") {
bp.src = "https://zz.bdstatic.com/linksubmit/push.js";
} else {
bp.src = "http://push.zhanzhang.baidu.com/push.js";
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
</script>

sitemap 이 읽혀지지 않는다면 head 에 위 스크립트를 추가해주자.

여담

사이트 등록 중 추가 정보를 입력이 필요하면 여기서 webmaster 관리자정보를 수정하면 된다. image from hexo

이메일의 경우만 인증이 들어감으로 QQ 와 Wechat 의 경우는 대충 써주면 된다.

curl을 사용하지 않는 http 통신

· 약 3분

curl 기능이 확장되어있지 않아 http 통신을 할 수 없는 경우가 있다. fsockopen 메소드를 사용해 같은 기능을 할 수 있다.

GET

<?php
/**
* [getData 외부파일을 GET 방식으로 읽기]
* @param [string] $str [url]
* @param [array] $data [parameters]
* @return [string] [내용]
*/
function getData($str, $data) {
$url = parse_url($str);

switch(strtoupper($url['scheme'])) {
case 'HTTP':
if (!isset($url['port'])) {
$url['port'] = 80;
}
break;
case 'HTTPS':
$url['ssl'] = 'ssl://';
if (!isset($url['port'])) {
$url['port'] = 443;
}
break;
}

$fp = @fsockopen($url['ssl'].$url['host'], $url['port'], $errno, $errstr, 10);
if ($fp) {
fwrite($fp, "GET $url[path]?".http_build_query($data)." HTTP/1.0\r\nHost: $url[host]\r\n");
while (!feof($fp)) {
$out .= fread($fp, 1024);
}

fclose($fp);
$out = explode("\r\n\r\n",$out);
array_shift($out);
$out = implode("",$out);
}

return $out;
}

POST

<?php
<?
/**
* [postData 외부파일을 POST 방식으로 읽기]
* @param [string] $str [url]
* @param [array] $data [parameters]
* @param [int] $sleepMs [연결지연ms]
* @return [string] [내용]
*/
function postData($str, $data, $sleepMs=0) {
$url = parse_url($str);

switch(strtoupper($url['scheme'])) {
case 'HTTP':
if (!isset($url['port'])) {
$url['port'] = 80;
}
break;
case 'HTTPS':
$url['ssl'] = 'ssl://';
if (!isset($url['port'])) {
$url['port'] = 443;
}
break;
}

$data_string = http_build_query($data);
$referrer = $_SERVER['SCRIPT_URL'];

$request = "POST {$url[path]} HTTP/1.1\r\nHost: {$url[host]}\r\nReferer: {$referrer}\r\n";
$request .= "Content-type: application/x-www-form-urlencoded\r\n";
$request .= "Content-length: " . strlen($data_string) . "\r\n";
$request .= "Connection: close\r\n\r\n";
$request .= "{$data_string}\r\n";

$fp = @fsockopen($url['ssl'].$url[host], $url[port], $errno, $errstr, 10);
if ($fp) {
fwrite($fp, $request);
usleep(($sleepMs * 1000));

do {
$header .= fread($fp, 1);
} while (!preg_match('/\\r\\n\\r\\n$/', $header));

if (preg_match('/Transfer\\-Encoding:\\s+chunked\\r\\n/', $header, $matches)) {
// check encoding
do {
$byte = $chunk_size = "";
do {
$chunk_size .= $byte; $byte = fread($fp, 1);
} while ($byte != "\r");
fread($fp, 1);
$chunk_size = hexdec($chunk_size);
if ($chunk_size){
$out .= @fread($fp, $chunk_size);
}
fread($fp, 2);
} while ($chunk_size);

} else if (preg_match('/Content\\-Length:\\s+([0-9]*)\\r\\n/', $header, $matches)) {
$out = fread($fp,$matches[1]);

} else {
while (!feof($fp)) {
$out .= fread($fp, 4096);
}
}

fclose($fp);
}

return $out;
}

예제

<?php
$param = array(
'data1' => '1',
'data2' => '2'
);

// get 요청
$get_data = getData('http://your.apiurl.com', $param);
// post 요청
$post_data = postData('http://your.apiurl.com', $param);
?>

설명

fsockopen 으로 소켓을 연 뒤 해당 host 로 request 만들어 전송한다.

stream 을 사용해 통신하는 방법도 있다.

sleep()을 사용해야할 때

· 약 2분

php에서 sleep() 메소드의 사용법은 이렇다.

1sec_delay.php
<?php
// sleep ( int $seconds )
sleep(1);

// usleep ( int $micro_seconds )
usleep(1000000);

단순히 시간을 지연시키는 이 메소드를 어디에 사용하는 걸까?

Curl 또는 file_get_contents로 내용을 가져올 때 지연없이 request를 보내면 차단을 당하거나 정상적인 응답이 오지 않을 수 있다. 요청을 보낸 후 sleep을 사용해 지연호출을 한다.

Crawling

웹 크롤링 중 호출이 일정시간이상되야 응답을 돌려주는 경우가 있다. 요청을 닫기 전에 sleep을 사용해 연결시간을 늘려준다.

Batch Update

많은 데이터를 cron을 사용해 update를 할 때 테이블이 Lock이 되는 경우를 방지하기 위해 사용한다.

<?php
foreach ($dummy as $data) {
// 5초가 걸리는 쿼리
DB::query("UPDATE ...");

// 5초를 지연시켜 그동안에 호출된 다른 로직을 실행할 수 있게 한다.
sleep(5);
}

이 방법보다 테이블이 Lock 되지않게 Update Query를 만드 것이 더 바람직하다.

id에 .(pariod)이 있는 element querySelector

· 약 1분

document.querySelector를 사용해 id에 .이 포함된 element를 선택시

<div id="id.has.pariod"></div>
<script>
var $div = document.querySelector("#id.has.pariod");
console.log($div);
// => undefined;
</script>

undefined를 반환하며 선택이 되지 않는다. .(pariod)는 class 선택자라 중복이되어 발생하는 오류이다.

해결

.에 역슬래시를 2개 붙혀 escape 시킨다.

const $div = document.querySelector("#id\\.has\\.pariod");
console.log($div);
// => <div id="id.has.pariod"></div>

Vimeo Upload API - 2. PHP API

· 약 8분

Vimeo Developers 등록에서 이어집니다.

Vimeo Libraries에서 원하는 API를 선택하면 된다. Server Libraries PHP에서 Download를 클릭하면 Github로 이동하고 Installation을 참조하면 된다.

composer

composer가 있으면 쉽게 설치할 수 있다.

$ composer require vimeo/vimeo-api

직접 설치

v1.2.5를 다운 받고 원하는 곳에 압축을 푼 뒤 class를 load한다.

<?php
require("/path/to/vimeo.php/autoload.php");
?>

설정 변경

vimeo.php\config.json 파일을 열어 client_id, client_secret, access_token 값을 수정하자.

Upload API 사용

vimeo.php\example\upload.php를 커스터마이징 해보자.

기본 예제 소스

example\upload.php
<?php
use Vimeo\Vimeo;
use Vimeo\Exceptions\VimeoUploadException;

$config = require(__DIR__ . '/init.php');
if (empty($config['access_token'])) {
// 액세스 토큰이 없을시 로깅 로직 추가
// throw new Exception('액세스 토큰 없이 업로드 할 수 없다.
// 개발자 앱 페이지에서 액세스 토큰을 발급 받거나 auth.php를 이용해라.');
}
$lib = new Vimeo($config['client_id'], $config['client_secret'], $config['access_token']);
// argv 대신 업로드할 파일경로의 배열을 넣으면 된다.
$files = $argv;
array_shift($files);
// 업로드된 트랙을 체크하는 배열
$uploaded = array();
// 업로드 로직 시작
foreach ($files as $file_name) {
// 업로드 시작 로깅
// print 'Uploading ' . $file_name . "\n";
try {
// Vimeo 서버에 업로드하고 해당 파일의 uri을 받는다.
$uri = $lib->upload($file_name);
// 해당 파일의 정보를 요청한다.
$video_data = $lib->request($uri);
// 업로드가 성공한 파일의 링크를 받는다.
$link = '';
if($video_data['status'] == 200) {
$link = $video_data['body']['link'];
}
// 로깅을 위해 저장
$uploaded[] = array('file' => $file_name, 'api_video_uri' => $uri, 'link' => $link);
}
catch (VimeoUploadException $e) {
// 업로드 오류 발생시 예외처리
// print 'Error uploading ' . $file_name . "\n";
// print 'Server reported: ' . $e->getMessage() . "\n";
}
}
// 결과 로깅 및 파일 링크 표시
// print 'Uploaded ' . count($uploaded) . " files.\n\n";
// foreach ($uploaded as $site_video) {
// extract($site_video);
// print "$file is at $link.\n";
// }

커스터마이징

25번째 줄의 request 요청에 추가로 영상정보를 변경하게 요청할 수 있다. 영상정보를 가져오는건 변하지 않는다.

<?php
/**
* [lib->request function]
* 해당 URI를 대상으로 데이터를 받아오고
* PATCH 메소드를 이용해서 그 동영상의 Metadata를 수정하는 함수
*
* @param {[string]} uri [업로드 된 동영상 주소]
* @param {[array]} options [파라미터 배열(json 형식의 배열타입)]
* @param {[string]} method [요청할 메소드 타입(수정은 PATCH)]
* @return {[video]} video_data [비디오 데이터]
*/
$video_data = $lib->request($uri, array(
'name' => '동영상 이름',
'description' => '동영상 설명',
'embed' => array( // embed시의 옵션
'buttons' => array(
'like' => false, // 좋아요 버튼
'watchlater'=> false, // 나중에보기 버튼
'share' => false, // 공유 버튼
'embed' => false, // embed 버튼
'fullscreen'=> false // 전체화면 버튼
),
'logos' => array(
'vimeo' => false // 비메로 로고
),
'title' => array(
'owner' => 'hide', // 제작자 정보
'portrait' => 'hide', // 제작자 로고
'name' => 'hide' // 제작자 이름
),
'playbar' => false // 재생바
)
), 'PATCH');
?>

옵션은 해당 endpoints 참조하자. image from hexo

video_data endpoint

업로드된 파일의 response data에는 파일 metadata가 들어있다. $video_data['body']['키값'] 으로 접근하면 된다. 입맛에 맞게 사용해보자.

{
"uri": "영상 vimeo 링크",
"name": "영상 제목",
"description": "영상 설명",
"link": "영상 vimeo 링크",
"duration": 재생 시간,
"width": 가로 크기,
"language": 언어,
"height": 세로 크기,
"embed": {
"uri": null,
"html": "iframe 영상 링크",
"buttons": {
"like": 좋아요 버튼 여부,
"watchlater": 나중에보기 버튼 여부,
"share": 공유 버튼 여부,
"embed": embed 버튼 여부,
"hd": 고화질 버튼 여부,
"fullscreen": 전체화면 버튼 여부,
"scaling": 스케일링 버튼 여부
},
"logos": {
"vimeo": 비메오 로고 여부,
"custom": {
"active": 커스텀 로고 여부,
"link": 커스텀 로고 링크,
"sticky": sticky 여부
}
},
"title": {
"name": 제작자 이름 display 설정,
"owner": 제작자 정보 display 설정,
"portrait": 제작자 로고 display 설정
},
"playbar": 재생바 여부,
"volume": 볼륨바 여부,
"color": "색상 헥스코드"
},
"created_time": "생성일시 gmt",
"modified_time": "변경일시 gmt",
"release_time": "노출일시 gmt",
"content_rating": [
"safe"
],
"license": "라이센스",
"privacy": {
"view": "노출 설정",
"embed": "embed 노출 설정",
"download": 다운로드 여부,
"add": 추가 여부,
"comments": "댓글 허용 설정"
},
"pictures": {
"uri": "영상 대표 사진 링크",
"active": 사진 여부,
"type": "custom",
"sizes": [
{
"width": 100,
"height": 75,
"link": "사이즈별 링크"
},
...
],
"resource_key": "접근 키"
},
"tags": [태그 배열],
"stats": {
"plays": 재생 수
},
"metadata": {
"connections": {
"comments": {
"uri": "댓글 링크",
"options": [
"GET",
"POST"
],
"total": 0
},
"credits": {
"uri": "크레딧 링크",
"options": [
"GET",
"POST"
],
"total": 0
},
"likes": {
"uri": "좋아요 링크",
"options": [
"GET"
],
"total": 0
},
"pictures": {
"uri": "사진 링크",
"options": [
"GET",
"POST"
],
"total": 0
},
"texttracks": {
"uri": "텍스트 트랙 링크",
"options": [
"GET",
"POST"
],
"total": 0
},
"related": null
},
"interactions": {
"watchlater": {
"added": false,
"added_time": null,
"uri": "나중에보기 링크"
}
}
},
"user": {
"유저정보 및 접근 경로"
},
"review_link": "리뷰 다이렉트 링크",
"files": [
{
"quality": "파일 화질",
"type": "파일 타입",
"width": 파일 가로,
"height": 파일 세로,
"link": "파일 다이렉트 링크",
"created_time": "파일 생성일시",
"fps": 주사율,
"size": 파일 크기,
"md5": "토큰",
"link_secure": "파일 다이렉트 시큐어 링크"
},
...
],
"download": [
{
"quality": "다운로드 파일 화질",
"type": "파일 타입",
"width": 파일 가로,
"height": 파일 세로,
"expires": "만료일시",
"link": "다운로드 다이렉트 링크",
"created_time": "생성일시",
"fps": 주사율,
"size": 파일 크기,
"md5": "토큰"
},
...
],
"app": {
"name": "앱 이름",
"uri": "앱 링크"
},
"status": "영상 상태",
"resource_key": "리소스 키",
"embed_presets": null
}

여담

API의 내부 구조는 헤더추가와 curl로만 이루어져있다. OAuth2.0과 RESTful를 이해했다면 아주 쉬울 것이다.

Vimeo Upload API - 1. Vimeo Developers

· 약 4분

비메오 개발자 커뮤니티 로그인 후 MyApps > Create New App 버튼을 클릭한다. image from hexo 사용할 앱의 이름, 설명, URL 경로와 앱 로고 URL을 등록한다. App Callback URL은 하나의 계정안에 멀티 유저를 두고 각자의 유저명으로 여러 동영상을 등록할시 OAuth2.0 인증을 통해 인증을 받기위한 것인데, 단순한 업로드 로직일 경우에는 필요가 없다. 앱에서 업로드가 있는 폴더안으로 경로만 대충 잡아주면 된다.

권한 신청

생성된 앱으로 들어가 Request Upload Access를 클릭한다. image from hexo image from hexo 1번은 이 앱(test)에 요금을 받을 것인지를 묻는다. Yes 클릭시 비메오측 승인이 없으면 요금청구를 할 수 없다는 경고문과 함께 어떻게 차징을 할지 이유를 쓰는 란이 생긴다. 업로드로 과금을 받진 않을 것이니 No를 선택하자.

2번은 업로더의 계정으로 업로드를 할 것인지, 내 계정으로만 업로드를 할 것인지를 선택한다. 전자를 선택시에 Callback URL을 필히 설정해주어야하고 Callback URL에 Return된 Multi-User의 Access Token을 가지고 분기 업로드를 해주는 로직을 구현해야 한다. 하지만 우리는 한가지 계정에서의 비디오 업로드를 구현하기에 후자를 선택하면 된다.

My account 선택시 비디오의 제작자를 묻는 선택지가 나오는데 선택해준다.

3번은 어떤 종류에 대한 비디오가 업로드가 될지 영문으로 설명을 해야한다. 간단히 "test용이다" 라고 적으니 Reject메일이 날라오는 걸로 보아 구체적으로 적어야한다. 샘플 동영상을 링크 걸어주는게 가장 간단한 인증방법이 될 것이다.

Request Upload Access를 클릭하면 5영업일 내에 처리해주겠다는 상태가 된다. image from hexo

토큰 생성

생성된 앱에서 Authentication 메뉴로 들어가 scope를 설정하고 Generate Token 버튼을 클릭한다. image from hexo Create는 채널을 만드는거라 필요없고 Edit, Delete, Upload 권한정도를 설정하면 된다.

image from hexo Access Token, Client Identifier, Client Secret를 모두 저장해둔다. Access Token은 분실시 다시 확인할 수 없고, 재발급만 가능하니 잘 간직하자.

회신 기다리기

회원가입시 등록한 메일 주소로 Vimeo 측에서 Upload Access에 대한 회신메일이 온다. Reject시 상세한 이유가 Approved시 환영한다라는 내용이다. 조건을 충족해 승인 완료가 되어 해당 앱에 다시 들어가보면 아래처럼 상태가 바뀐다. image from hexo

2. PHP API 사용으로 이어집니다.

Javascript로 로컬에 이미지 다운로드

· 약 4분

버튼을 눌러 이미지를 다운로드 받고 싶다면 어떻게 해야될까? Client Side 에서 할 수 있는 가장 쉬운 방법은 download attribute를 사용하는 것이다. 하지만 예상했듯이 이 속성은 IE 에서 지원하지 않는다.

base64

먼저 이미지를 base64 인코딩해야한다. FileReader 또는 Canvas 를 이용해 변환할 수 있는데 stackoverflow 의 명쾌한 답변을 참조하자. jQuery 환경이라면 Reading binary data using jquery ajax를 참조해도 된다.

base64encode 가 된 이미지 데이터가 뽑아지면 시작할 수 있다.

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPYAAAD2CAAAAADAeSUUAAADN0lEQVR42u3aQY7CMBAEQP7/afaKtEu2exwk4lROKITEZSQGT/vxiI/ny/H7zOv513ffXZl89viad6M6+cDGxsa+CDsZUP5uMqx3d8gntB3nH1ODjY2NvR373S/88TUtMuG1E5qMExsbGxv7eChJUTl+4nP5wMbGxsZu2UlbJy9RyQS1SxpsbGzsu7FXCkPeTkoihGTQeUPqhF4aNjY29tez81T0+19/JN/GxsbG/mL2rHEzi2OTxc968BwpsLGxsTdiz0Lc/I9+voBprzwhhMbGxsbeiN1S2xvNWkuzZtD6aLGxsbGvxW4f0BaqtpU/C4/bhRA2Njb2TuwkTF0ZVn4+/+zKJEb7lbCxsbG3YM9aRe0CJo8NkivzFhU2Njb2ruwW3/71r0vL6FnDXUvY2NjYl2XPAto8mm0LUrIImcUG2NjY2Luy1xv6eTCwMomzAlZPEzY2NvbF2ceDWIld82XJSjjRbsfExsbG3om9EgbkDak2GM5L6bApho2Njb0F+/imeclJWj/t61nwEBU2bGxs7I3Yn2vifKLN1MKKxQk2Njb2ZdlnFYzZ9bMG00rbCxsbG/sO7Lw13261zIvlMXVW5B7tDGFjY2NvwZ4lpDmpLYenXYmNjY29ETv5u5+XkDYYaFtL7ZKm2KmEjY2NfVl2u10mWYS009q2hNoC9scZbGxs7I3YM+SsjJ3V0lqZ3MfKd4uNjY39lexk6Hlkm4fHs40+7dacomhhY2Njb8TOi1MeCZw7oflER1t2sLGxsS/Onq1d2hC3XYq0japZCIGNjY29K7u9Xds8WmlU5SWwiH6xsbGxt2DnP/dthNBS12FFScPGxsbelN02aNrFRh4tzJ5SbO7BxsbGvgE7Aa8EACvlc6X9hI2Njb0T+1keySBmm3jy0Le92z9JCDY2NvbF2W0BOLfwHJ9vW0v1UgQbGxt7I3ZStFbKTLKMab+TZFH0T+SAjY2NvR07b7vP4ti2TK5MSvRcbGxs7Nuz2wfnT1nZalncBxsbG/vG7PWtlmdt5VlpimFjY2Pvx06aRHlBarfs5O3+9ut5u/rAxsbG3og9W1TkxWYWP7T3zBtV2NjY2BuxfwBOAYaS/PUQNQAAAABJRU5ErkJggg==

blob

이제 이미지 데이터를 Blob Object 로 변환해야한다.

// imageSrc는 위의 이미지 데이터이다.
// 실제 데이터는 iVBO...부터이므로 split한다.
const imgData = atob(imageSrc.split(",")[1]);
const len = imgData.length;
const buf = new ArrayBuffer(len); // 비트를 담을 버퍼를 만든다.
const view = new Uint8Array(buf); // 버퍼를 8bit Unsigned Int로 담는다.
let blob, i;

for (i = 0; i < len; i++) {
view[i] = imgData.charCodeAt(i) & 0xff; // 비트 마스킹을 통해 msb를 보호한다.
}
// Blob 객체를 image/png 타입으로 생성한다. (application/octet-stream도 가능)
blob = new Blob([view], { type: "image/png" });

msSaveOrOpenBlob

IE10+ 에서 사용가능한 msSaveOrOpenBlob가 필요하다.

msSaveBlob 메서드는 저장 단추만 사용자에게 제공하는 반면 msSaveOrOpenBlob 메소드는 저장 및 열기 단추를 모두 제공한다는 것이 차이점이다.

// blob과 저장될 파일명을 받는다.
window.navigator.msSaveOrOpenBlob(blob, "new_file_name.png");

Not IE

IE 가 세상에 존재하지 않는다면 코드는 아주 예쁘게 짤 수 있다.

<a href="data:image/png;base64,iVBORw0KG..." download="new_file_name.png">
<img src="data:image/png;base64,iVBORw0KG..." />
</a>

a 태그가 보기 싫다면 아래 구문을 포함해 함수로 만든다.

const a = document.createElement("a");
a.style = "display: none";
a.href = img.src;
a.download = "new_file_name.png";

document.body.appendChild(a);
a.click();

setTimeout(function () {
// 다운로드가 안되는 경우 방지
document.body.removeChild(a);
}, 100);

Blob 재사용

IE 때문에 만들었던 Blob 객체를 URL 기능을 이용해 재사용해보자.

const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.style = "display: none";
a.href = url;
a.download = "new_file_name.png";

document.body.appendChild(a);
a.click();

setTimeout(function () {
document.body.removeChild(a);
URL.revokeObjectURL(url); // 메모리 해제
}, 100);

소스

/**
* [downloadImage]
* @param {[string]} img [base64encoded image data]
* @param {[string]} fileName [new file name]
* @return [image file]
*/
function downloadImage(img, fileName) {
let imgData = atob(img.src.split(",")[1]),
len = imgData.length,
buf = new ArrayBuffer(len),
view = new Uint8Array(buf),
blob,
i;

for (i = 0; i < len; i++) {
view[i] = imgData.charCodeAt(i) & 0xff; // masking
}

blob = new Blob([view], {
type: "application/octet-stream",
});

if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(blob, fileName);
} else {
//var url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.style = "display: none";
//a.href = url;
a.href = img.src;
a.download = fileName;
document.body.appendChild(a);
a.click();

setTimeout(function () {
document.body.removeChild(a);
//URL.revokeObjectURL(url);
}, 100);
}
}

여담

![image from hexo](https://i.imgur.com/36Rf76J.gif title:"실망시키지 않는 IE")

XML-RPC를 활용한 네이버 블로그 글쓰기 API

· 약 2분

OAuth 를 이용한 블로그 API 가 있는데, 네이버 블로그 글쓰기는 그보다 더 간단히 구현할 수 있다.

XML-RPC에서 다운받는다. image from hexo

압출을 푼 뒤 lib/xmlrpc.inc 파일을 로직을 만들 곳에 복사해 놓는다.

네이버 블로그 플러그인 설정

관리 > 글쓰기 API 설정 메뉴로 들어간다. image from hexo

사용하기로 설정을 바꾼 후 비밀번호를 기억하고, 어디에 등록할지 카테고리를 정한다. image from hexo

API 연동

<?php

include_once "xmlrpc.inc";

$title = $_POST['title'];
$content = $_POST['content'];
$result = newPost($title, $content);

echo json_encode($result);
// 블로그 API 함수
function newPost($title, $description) {
$g_blog_url = "https://api.blog.naver.com/xmlrpc";
$user_id = "아이디";
$blogid = "아이디";
$password = "발급받은 비밀번호";
$publish = true;
$client = new xmlrpc_client($g_blog_url);
$client->setSSLVerifyPeer(false);

$GLOBALS['xmlrpc_internalencoding'] = 'UTF-8';

$struct = [
'title' => new xmlrpcval($title, "string"),
'description' => new xmlrpcval($description, "string")
];

$f = new xmlrpcmsg("metaWeblog.newPost",
[
new xmlrpcval($blogid, "string"),
new xmlrpcval($user_id, "string"),
new xmlrpcval($password, "string"),
new xmlrpcval($struct , "struct"),
new xmlrpcval($publish, "boolean")
]
);

$f->request_charset_encoding = 'UTF-8';
return $response = $client->send($f);
}
?>

네이버 블로그는 html 태그가 먹으므로 escape 처리를 할 필요는 없다.

RESTful을 위한 PHP HTTP Method 처리

· 약 1분

메소드를 분기해서 새로운 배열에 담아주면 된다.

contentType을 application/json으로 보냈을 경우

<?
$method = strtolower($_SERVER['REQUEST_METHOD']);
switch ($method) {
case 'get':
$input = json_decode(json_encode($_GET), true);
break;
case 'post':
$input = json_decode(file_get_contents('php://input'), true);
break;
case 'put':
$input = json_decode(file_get_contents('php://input'), true);
break;
case 'delete':
$input = json_decode(file_get_contents('php://input'), true);
break;
}
?>

contentType을 그냥 전송한다면 **file_get_contents('php://input')**으로 받으면 된다.

여담

GET을 제외한 method는 params를 JSON.stringify한 뒤에 file_get_contents로 읽어오면 되는데, GET으로는 file전송을 할 수 없기 때문에 위처럼 받으면 된다.