openresty 이미지(파일)서버 구현

38953 단어 짜임새
소개 하 다.
앞 순서
이 기능 은 openresty 의 lua 스 크 립 트 를 이용 하여 이 루어 진 그림(파일)저장 기능 으로 파일 업로드 에 자바 코드 를 사용 하여 개발 한 것 입 니 다.
데이터 정의
업로드 데이터 와 파일 정 보 는 앞 뒤 를 가리 지 않 지만 시스템 은 마지막 한 쌍 의 정보 만 저장 합 니 다.
  • 데이터 형식:
  • {"fileDir":"       ","fileName":"   "}
  • 복귀 결과
  • {"status":"    ","result":"    ","msg":"    "}
    enum status:["success","failed"]
  • 폴 더 가 저 장 된 폴 더 를 저장 하고 nginx 의 perfix 변수 에서 정의 합 니 다
  • 코드 구현
    Nginx 설정
    다음 과 같다.
    server {
        listen       80;
        server_name  localhost;
    #         
        set $prefix "/data";
    
        location /uploadimage {
    #       lua     ,       
    #       lua_code_cache off;
    #   lua  
            content_by_lua_file /openresty-web/luascript/luascript;
        }
    #          nginx     
        location /uploadtest{
    #       lua_code_cache off;
            content_by_lua_file /openresty-web/luascript/luauploadtest;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }

    lua 스 크 립 트
    luascript:
    package.path = '/openresty-web/lualib/resty/?.lua;'
    local upload = require "upload"
    local cjson = require("cjson")
    
    Result={status="success",result="",msg=""}
    Result.__index=Result
    function Result.conSuccess(ret)
        ret["status"]="success"
        ret["result"]="upload success"
        return ret
    end
    
    function Result.conFailed(ret,err)
        ret["status"]="failed"
        ret["msg"]=err
        ret["result"]="upload failed"
        return ret
    end
    
    function Result:new()
        local ret={}
        setmetatable({},Result)
        return ret
    end
    
    -- lua-resty-upload
    local chunk_size = 4096
    local form = upload:new(chunk_size)
    if not form then
        ngx.say(cjson.encode(Result.conFailed(Result:new(),"plase upload right info")))
        return 
    end
    local file
    local filelen=0
    form:set_timeout(0) -- 1 sec
    local filename
    local prefix=ngx.var.prefix
    
    --      ,               
    function get_filename(res)
        local filename = ngx.re.match(res,'(.+)filename="(.+)"(.*)')
        if filename then 
            return filename[2]
        end
    end
    
    
    --        ,            
    function openstream(fileinfo,opt)
        local file,err=io.open(prefix..fileinfo["fileDir"],"r")
        if not file then
            local start=string.find(err,"No such file or directory")
            if start then
                local exeret=os.execute("mkdir -p "..prefix..fileinfo["fileDir"])
                if exeret ~= 0 then
                    return nil,"Make directory failed"
                end
            else
                return nil,err
            end
        end
        file,err=io.open(prefix..fileinfo["fileDir"]..fileinfo["fileName"],opt)
        return file,err
    end
    
    local osfilepath
    local tmpfiletbl
    local hasFile=false
    local loopfile=false
    local fileinfostr
    local fileinfo
    local result=Result:new()
    --            
    while true do
        local typ, res, err = form:read()
        if not typ then
            break
        end
        if typ == "header" then
            if res[1] ~= "Content-Type" then
                filename = get_filename(res[2])
                if filename then
                    loopfile=true
                    hasFile=true
                    --          
                    --         
                    if fileinfo then
                        file,err=openstream(fileinfo,"w")
                        if not file then
                            break
                        end
                    else
                        tmpfiletbl={}
                    end
                else
                    loopfile = false
                    fileinfostr = ""
                end
            end
        end
        if loopfile then
            if typ == "body" then
                if file then
                    filelen= filelen + tonumber(string.len(res))    
                    file:write(res)
                else
                    table.insert(tmpfiletbl,res)
                end
            elseif typ == "part_end" then
                if file then
                    file:close()
                    file = nil
                end
            end
        else
            if typ == "body" then
                fileinfostr=fileinfostr .. res
            elseif typ == "part_end" then
                fileinfo = cjson.decode(fileinfostr)
            end
        end
        if typ == "eof" then
            break
        end
    end
    
    if not hasFile then
        err="plase upload file"
    elseif not fileinfo or not fileinfo["fileDir"] or not fileinfo["fileName"] then
        err="plase offer file info"
    end
    
    if err then
        ngx.log(ngx.ERR,err)
        Result.conFailed(result,err)
        ngx.say(cjson.encode(result))
        return 
    end
    
    --                
    --                      
    if tmpfiletbl and table.getn(tmpfiletbl) > 0 then
        file,err=openstream(fileinfo,"w")
        if not file then
            ngx.log(ngx.ERR,err)
            Result.conFailed(result,err)
            ngx.say(cjson.encode(result))
            return 
        else
            for index,value in ipairs(tmpfiletbl)
            do
                filelen= filelen + tonumber(string.len(value)) 
                file:write(value)
            end
            file:close()
            file=nil
        end
    end
    
    
    Result.conSuccess(result)
    ngx.say(cjson.encode(result))
    

    luauploadtest:
    local upload = require "resty.upload"
    local cjson = require "cjson"
    
    local chunk_size = 5 -- should be set to 4096 or 8192
                         -- for real-world settings
    
    local form, err = upload:new(chunk_size)
    if not form then
        ngx.log(ngx.ERR, "failed to new upload: ", err)
        ngx.exit(500)
    end
    
    form:set_timeout(1000) -- 1 sec
    
    while true do
        local typ, res, err = form:read()
        if not typ then
            ngx.say("failed to read: ", err)
            return
        end
    
        ngx.say("read: ", cjson.encode({typ, res}))
    
        if typ == "eof" then
            break
        end
    end
    
    local typ, res, err = form:read()
    ngx.say("read: ", cjson.encode({typ, res}))
    

    luauploadtest 코드 는 공식 제공 코드 입 니 다.
    Java
    ImageServer:
    package cn.com.cgbchina.image;
    
    import cn.com.cgbchina.image.exception.ImageDeleteException;
    import cn.com.cgbchina.image.exception.ImageUploadException;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * Created by 11140721050130 on 16-3-22.
     */
    public interface ImageServer {
        /**
         *     
         *
         * @param fileName    
         * @return       
         */
        boolean delete(String fileName) throws ImageDeleteException;
    
        /**
         *
         * @param originalName      
         * @param file   
         * @return           
         */
        String upload(String originalName, MultipartFile file) throws ImageUploadException;
    }
    

    LuaResult:
    package cn.com.cgbchina.image.nginx;
    
    import lombok.Getter;
    import lombok.Setter;
    
    /**
     * Comment:         ,
     *       LuaImageServiceImpl     ,
     *   Jackson   ,      
     * Created by ldaokun2006 on 2017/10/24.
     */
    @Setter
    @Getter
    public class LuaResult{
        private LuaResultStatus status;
        private String result;
        private String msg;
        private String httpUrl;
        public LuaResult(){}
    
        public void setStatus(String result){
            status=LuaResultStatus.valueOf(result.toUpperCase());
        }
        public enum LuaResultStatus{
            SUCCESS,FAILED;
        }
    }
    

    ImageServerImpl:
    package cn.com.cgbchina.image.nginx;
    
    import cn.com.cgbchina.common.utils.DateHelper;
    import cn.com.cgbchina.image.ImageServer;
    import cn.com.cgbchina.image.exception.ImageDeleteException;
    import cn.com.cgbchina.image.exception.ImageUploadException;
    import com.github.kevinsawicki.http.HttpRequest;
    import com.google.common.base.Splitter;
    import com.spirit.util.JsonMapper;
    import lombok.AllArgsConstructor;
    import lombok.Getter;
    import lombok.Setter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.UUID;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * Comment:         
     * Created by ldaokun2006 on 2017/10/16.
     */
    @Service
    @Slf4j
    public class LuaImageServiceImpl implements ImageServer{
        //   nginx   url ,               
        private List httpUrls;
        private ExecutorService fixedThreadPool ;
        private Integer timeout;
        private int threadSize=50;
    
        public LuaImageServiceImpl(String httpUrls){
            this(httpUrls,30000);
        }
    
        /**
         *
         * @param httpUrls   nginx   url
         * @param timeout http    
         */
        public LuaImageServiceImpl(String httpUrls,int timeout){
            this.httpUrls=Splitter.on(";").splitToList(httpUrls);
            //     ,             
            this.fixedThreadPool= new ThreadPoolExecutor(threadSize, threadSize,
                    0L, TimeUnit.MILLISECONDS,
                    new LinkedBlockingQueue(),new ThreadFactory(){
                        private final AtomicInteger poolNumber = new AtomicInteger(1);
                        private final ThreadGroup group;
                        private final AtomicInteger threadNumber = new AtomicInteger(1);
                        private final String namePrefix;
    
                        {
                            SecurityManager s = System.getSecurityManager();
                            group = (s != null) ? s.getThreadGroup() :
                                    Thread.currentThread().getThreadGroup();
                            namePrefix = "LuaUploadPool-" +
                                    poolNumber.getAndIncrement() +
                                    "-thread-";
                        }
    
                        public Thread newThread(Runnable r) {
                            Thread t = new Thread(group, r,
                                    namePrefix + threadNumber.getAndIncrement(),
                                    0);
                            if (t.isDaemon())
                                t.setDaemon(false);
                            if (t.getPriority() != Thread.NORM_PRIORITY)
                                t.setPriority(Thread.NORM_PRIORITY);
                            return t;
                        }
                    });
            this.timeout=timeout;
        }
    
        /**
         * Comment:          
         * @param fileName    
         * @return
         * @throws ImageDeleteException
         */
        @Override
        public boolean delete(String fileName) throws ImageDeleteException {
            return true;
        }
    
        /**
         * Commont:    SpringMVC 
         * @param originalName      
         * @param file   
         * @return
         * @throws ImageUploadException
         */
        @Override
        public String upload(String originalName, MultipartFile file) throws ImageUploadException {
            try {
                return this.upload(originalName,file.getInputStream());
            } catch (IOException e) {
                log.error("upload fail : " + e.getMessage(), e);
                throw new ImageUploadException("upload fail : "+e.getMessage(),e);
            }
        }
    
        /**
         * Commont:         
         * @param originalName      
         * @param inputStream          
         * @return
         * @throws ImageUploadException
         */
        private String upload(String originalName,InputStream inputStream) throws ImageUploadException {
            ByteArrayOutputStream byteOutStream = null;
            try {
                //    
                byte[] tmpData=new byte[1024];
                byte[] inputData;
                byteOutStream = new ByteArrayOutputStream();
                int len=0;
                while((len=inputStream.read(tmpData,0,tmpData.length))!=-1){
                    byteOutStream.write(tmpData,0,len);
                }
                inputData=byteOutStream.toByteArray();
                LuaSend sendInfo = new LuaSend(generateFileDir(),generateFileName(originalName));
                List> resultList=new ArrayList<>(httpUrls.size());
    
                //    
                for(String httpUrl:httpUrls) {
                    SendImg sendImg = new SendImg(httpUrl,sendInfo, inputData,this.timeout);
                    resultList.add(fixedThreadPool.submit(sendImg));
                }
                for(Future future:resultList) {
                    //           
                    LuaResult resultLuaResult = future.get();
                    if (LuaResult.LuaResultStatus.SUCCESS != resultLuaResult.getStatus()) {
                        throw new ImageUploadException("lua result url:"+resultLuaResult.getHttpUrl()+" msg : " + resultLuaResult.getMsg());
                    }
                }
    
                return sendInfo.toString();
            }catch (Exception e){
                log.error("upload fail : "+e.getMessage(),e);
                throw new ImageUploadException("upload fail : "+e.getMessage(),e);
            }finally {
                try {
                    if(byteOutStream!=null) {
                        byteOutStream.close();
                    }
                    if(inputStream!=null) {
                        inputStream.close();
                    }
                } catch (IOException e) {
                    throw new ImageUploadException("upload fail : "+e.getMessage(),e);
                }
            }
        }
        String separator=File.separator;
        String dateFormat=separator+"yyyy"+separator+"MM"+separator+"dd"+ separator;
    
        /**
         * Comment:       ,            
         * @return         
         */
        private String generateFileDir(){
            return DateHelper.date2string(new Date(),dateFormat);
        }
    
        /**
         * Comment:  UUID       
         * @param originalName      
         * @return        
         */
        private String generateFileName(String originalName){
            return UUID.randomUUID().toString();
        }
    
        /**
         * Comment:        
         */
        @AllArgsConstructor
        class SendImg implements  Callable{
    
            private String httpUrl;
            private LuaSend sendInfo;
            private byte[] inputStream;
            private Integer timeout;
    
    
            @Override
            public LuaResult call() throws Exception {
                try {
                    String resultStr = HttpRequest
                            .post(httpUrl, false)
                            .part("fileInfo", JsonMapper.JSON_NON_EMPTY_MAPPER.toJson(sendInfo))
                            //        ,part            ,
                            //      Content-Type fileName 
                            .part("file", sendInfo.getFileName(), "multipart/form-data; boundary=00content0boundary00", new ByteArrayInputStream(inputStream))
                            .connectTimeout(timeout).body();
                    log.info("result:"+resultStr);
                    LuaResult result = JsonMapper.JSON_NON_DEFAULT_MAPPER.fromJson(resultStr, LuaResult.class);
                    result.setHttpUrl(httpUrl);
                    return result;
                }catch(Exception e){
                    throw new ImageUploadException("upload failed url:"+httpUrl+" info:"+sendInfo.toString(),e);
                }
            }
        }
    
        /**
         * Comment:    
         */
        @Setter
        @Getter
        @AllArgsConstructor
        class LuaSend {
            //     
            private String fileDir;
            //    
            private String fileName;
            @Override
            public String toString(){
                return fileDir+fileName;
            }
        }
    
    
        /**
         * Comment:   
         * @param args
         * @throws ImageUploadException
         * @throws FileNotFoundException
         */
        public static void main(String[] args) throws ImageUploadException, FileNotFoundException {
            LuaImageServiceImpl service=new LuaImageServiceImpl("http://192.168.99.102/uploadimage");
            try {
                System.out.println(service.upload("qqqqq", new FileInputStream("D:\\shsh.txt")));
            }finally {
                service.fixedThreadPool.shutdown();
            }
        }
    }
    

    총결산
    이 가능 하 다,~할 수 있다,...
  • 두 개의 사진 이나 사진 정 보 를 올 릴 때 시스템 은 마지막 정보 만 보류 합 니 다
  • 그림 과 그림 정 보 는 마음대로 배치 할 수 있 지만 이 두 가 지 는 반드시 쌍 을 이 루어 보 내야 합 니 다.먼저 그림 정 보 를 보 낸 후에 그림 을 보 내 는 것 을 권장 합 니 다.그러면 그림 은 lua 에 메모리 에 저장 하지 않 아 도 됩 니 다
  • 큰 그림 을 올 릴 때 파일 이 너무 큰 알림 이 나타 납 니 다.nginx 프로필 에 추가 해 야 합 니 다client_max_body_size 100M;
  • Http Header 의 Content-Type 은 사용 해 야 합 니 다multipart/form-data;
    boundary=00content0boundary00
    .boundary 는 존재 해 야 합 니 다.그렇지 않 으 면 사용 하기 어렵 습 니 다
  • 사진 전송 HttpRequest.part 업로드 사진 은 Content-type 과 fileName 을 명시 해 야 합 니 다.그렇지 않 으 면 사용 하기 어렵 지만 Content-type 은 예 를 들 지 않 아 도 됩 니 다
  • 사진 정 보 는 byte 형 으로 복사 해 야 합 니 다.다 중 스 레 드 를 사용 할 때 각자 보 내야 하기 때 문 입 니 다
  • 개발 중 에 닥 친 문제
  • 전송 사진 HttpRequest.part 업로드 사진 은 반드시 Content-type 을 써 야 합 니 다.그렇지 않 으 면 사용 하기 어렵 습 니 다
  • Jackson 과 fastjson 은 반 서열 화 를 필요 로 하 는 클래스 에 대해 반드시 구조 함수 가 있어 야 하 며 내부 클래스
  • 가 되 어 서 는 안 된다.
  • lua 의 string.find 를 찾 지 못 하면 되 돌아 오 는 결 과 는nil
  • CSDN 편집기,기능 이 좋 지 않 을 필요 가 없습니다
  • 지식 에 미치다
  • HttpRequest.part 업로드Content-type:multipart/form-data;
  • lua 의 사용:http://www.runoob.com/lua/lua-tutorial.html
  • openresty api:http://openresty.org/cn/components.html
  • 좋은 웹페이지 즐겨찾기