Dateien verwalten mit Modul jdk.httpserver
ulrich
2024-01-15 342aebc6b1143ab81a626a71b8bf7ce15eb5f361
src/de/uhilger/httpserver/cm/FileManager.java
@@ -17,41 +17,15 @@
 */
package de.uhilger.httpserver.cm;
import com.google.gson.Gson;
import com.sun.net.httpserver.Authenticator;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import de.uhilger.httpserver.base.HttpResponder;
import de.uhilger.httpserver.base.HttpHelper;
import de.uhilger.httpserver.base.handler.FileHandler;
import de.uhilger.httpserver.image.Datei;
import de.uhilger.httpserver.image.ImageActor;
import de.uhilger.httpserver.image.ImageThread;
import de.uhilger.httpserver.image.ImageThread.ThreadListener;
import de.uhilger.httpserver.cm.actor.Lister;
import de.uhilger.httpserver.cm.actor.Writer;
import de.uhilger.httpserver.oauth.BearerAuthenticator;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
 * <p>Der FileManager verknuepft einen HTTP-Endpunkt mit einem Ordner des lokalen
@@ -103,10 +77,7 @@
 * @author Ulrich Hilger
 * @version 1, 13. Mai 2021
 */
public class FileManager extends FileHandler implements ThreadListener {
  /* Der Logger fuer diesen ListFileHandler */
  private static final Logger logger = Logger.getLogger(FileManager.class.getName());
public class FileManager extends FileHandler {
  /*
  private static final String[] specialChars = {new String("\u00c4"), new String("\u00d6"),
@@ -134,21 +105,6 @@
  
  public static final String ATTR_ROLE = "role";
  
  private final List waitingThreads;
  private final int maxThreads;
  private int threadCount;
  //private String role;
  //public FileManager(String absoluteDirectoryPathAndName, String role, String ctx) {
  public FileManager() {
    //super(absoluteDirectoryPathAndName, ctx);
    //super(absoluteDirectoryPathAndName);
    waitingThreads = new ArrayList();
    maxThreads = 4;
    threadCount = 0;
    //this.role = role;
  }
  @Override
  public void handle(HttpExchange e) throws IOException {
    Authenticator a = e.getHttpContext().getAuthenticator();
@@ -158,697 +114,50 @@
      String userId = e.getPrincipal().getUsername();
      if(auth.hasRole(userId, e.getHttpContext().getAttributes().get(ATTR_ROLE).toString())) {
        String method = e.getRequestMethod();
        logger.fine("method: " + method);
        //logger.fine("method: " + method);
        HttpHelper helper = new HttpHelper();
        switch (method) {
          case HttpHelper.HTTP_GET:
            liste(e, helper);
            String path = e.getRequestURI().toString();
            if (path.endsWith(STR_SLASH)) {
              String json = new Lister().liste(helper.getFileName(e),
                      e.getHttpContext().getPath(),
                      e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(),
                      path);
              if(null != json) {
                HttpResponder r = new HttpResponder();
                r.antwortSenden(e, SC_OK, json);
              } else {
                emptyListResponse(e);
              }
            } else {
              new Lister().b64Action(helper.getFileName(e),
                      e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString());
              super.handle(e);
            }
            break;
          case HttpHelper.HTTP_PUT:
            put(e, helper);
            new Writer().put(e, helper);
            break;
          case HttpHelper.HTTP_POST:
            speichern(e, helper);
            new Writer().speichern(e, helper);
            break;
          case HttpHelper.HTTP_DELETE:
            loeschen(e, helper);
            new Writer().loeschen(e, helper);
            break;
        }
      } else {
        standardHeaderUndAntwort(e, SC_FORBIDDEN, "Fehlende Rolle.");
        new Writer().standardHeaderUndAntwort(e, SC_FORBIDDEN, "Fehlende Rolle.");
      }
    } else {
      standardHeaderUndAntwort(e, SC_FORBIDDEN, "Fehlende Rolle.");
      new Writer().standardHeaderUndAntwort(e, SC_FORBIDDEN, "Fehlende Rolle.");
    }
  }
  private void put(HttpExchange exchange, HttpHelper helper) throws IOException {
    String query = exchange.getRequestURI().getQuery();
    if (query != null) {
      String[] params = query.split("=");
      for (String param : params) {
        logger.fine("param: " + param);
      }
      switch (params[0]) {
        case P_COPY:
          copyOrMove(exchange, params[1], helper.getFileName(exchange), OP_COPY);
          break;
        case P_MOVE:
          copyOrMove(exchange, params[1], helper.getFileName(exchange), OP_MOVE);
          break;
        case P_DUPLICATE:
          if(Boolean.parseBoolean(params[1])) {
            String neuerDateiName = duplizieren(exchange, helper);
            logger.fine("neuer Name: " + neuerDateiName);
            standardHeaderUndAntwort(exchange, SC_OK, neuerDateiName);
          }
          break;
        case P_RENAME:
          String neuerDateiName = umbenennen(exchange, helper, params[1]);
          logger.fine("neuer Name: " + neuerDateiName);
          standardHeaderUndAntwort(exchange, SC_OK, neuerDateiName);
          break;
        case P_ZIP:
          String path = exchange.getRequestURI().toString();
          logger.fine(path);
          String antwort = packFolder(helper.getFileName(exchange), path, exchange);
          if(antwort.equalsIgnoreCase("ok")) {
            standardHeaderUndAntwort(exchange, SC_OK, antwort);
          } else {
            standardHeaderUndAntwort(exchange, SC_UNPROCESSABLE_ENTITY, antwort);
          }
          break;
        case P_UNZIP:
          path = exchange.getRequestURI().toString();
          logger.fine(path);
          antwort = extractZipfile(helper.getFileName(exchange), path, exchange);
          if(antwort.equalsIgnoreCase("ok")) {
            standardHeaderUndAntwort(exchange, SC_OK, antwort);
          } else {
            standardHeaderUndAntwort(exchange, SC_UNPROCESSABLE_ENTITY, antwort);
          }
          break;
      }
    } else {
      speichern(exchange, helper);
    }
  }
  public class DirList {
    private String pfad;
    private List<Datei> dateien;
    public String getPfad() {
      return pfad;
    }
    public void setPfad(String pfad) {
      this.pfad = pfad;
    }
    public List<Datei> getDateien() {
      return dateien;
    }
    public void setDateien(List<Datei> dateien) {
      this.dateien = dateien;
    }
  }
  private void liste(HttpExchange e, HttpHelper helper) throws IOException {
    String path = e.getRequestURI().toString();
    logger.fine(path);
    String fName = helper.getFileName(e);
    String dirListPath = e.getHttpContext().getPath() + fName;
    if (path.endsWith(STR_SLASH)) {
      logger.fine("fName: " + fName);
      File dir = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fName);
      logger.fine("absPath: " + dir.getAbsolutePath());
      File[] files = dir.listFiles(new ImageFileFilter());
      if(files != null && files.length > 0) {
        Arrays.sort(files);
        ArrayList liste = new ArrayList();
        for (File file : files) {
          Datei datei = new Datei();
          String dateiName = file.getName();
          datei.setName(dateiName);
          if (file.isDirectory()) {
            datei.setTyp(Datei.TYP_ORDNER);
          } else {
            datei.setTyp(Datei.TYP_DATEI);
          }
          //datei.setPfad(e.getHttpContext().getPath() + fName);
          String lowerName = dateiName.toLowerCase();
          if (lowerName.endsWith(ImageActor.JPEG)
                  || lowerName.endsWith(ImageActor.JPG)
                  || lowerName.endsWith(ImageActor.PNG)) {
            datei.setBild(true);
            String ext = dateiName.substring(dateiName.lastIndexOf(STR_DOT));
            String ohneExt = dateiName.substring(0, dateiName.lastIndexOf(STR_DOT));
            datei.setMiniurl(ohneExt + ImageActor.TN + ext);
            buildImgSrc(file, datei, ohneExt, ext);
          }
          liste.add(datei);
        }
        while(threadCount > 0) {
          try {
            Thread.sleep(50);
          } catch (InterruptedException ex) {
            Logger.getLogger(FileManager.class.getName()).log(Level.SEVERE, null, ex);
          }
        }
        if(liste.size() > 0) {
          DirList list = new DirList();
          list.setPfad(dirListPath);
          list.setDateien(liste);
          Gson gson = new Gson();
          //String json = gson.toJson(liste);
          String json = gson.toJson(list);
          //byte[] bytes = json.getBytes();
          //logger.fine("json: '" + json + "'");
          HttpResponder r = new HttpResponder();
          r.antwortSenden(e, SC_OK, json);
        } else {
          emptyListResponse(e);
        }
      } else {
        emptyListResponse(e);
      }
    } else {
      String lowerName = fName.toLowerCase();
      if(lowerName.contains(ImageActor.B64)) {
        ImageActor actor = new ImageActor();
        String fromName = fName.replace(ImageActor.B64, "");
        File fromFile = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fromName);
        File toFile = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fName);
        logger.fine("from " + fromFile.getAbsolutePath() + ", to " + toFile.getAbsolutePath());
        if(!toFile.exists()) {
          actor.b64Image(fromFile, toFile);
        }
        super.handle(e);
      } else {
        super.handle(e);
      }
    }
  }
  //  data:[<mime type>][;charset=<Zeichensatz>][;base64],<Daten>
  /*
  [So. Juni 13 13:23:32 MESZ 2021] FEIN:
  file: /home/ulrich/helix-files/bild-test/10419903-14-2-1920-r.jpg,
  relname: bild-test/10419903-14-2-1920-r.jpg, ohneExt: 10419903-14-2-1920-r, ext: .jpg (de.uhilger.helix.FileManager buildImgSrc)
  */
  private void buildImgSrc(File file, Datei datei, String ohneExt, String ext) throws IOException {
    //logger.fine("file: " + file.getAbsolutePath() + ", ohneExt: " + ohneExt + ", ext: " + ext);
    File dir = file.getParentFile();
    String newRelName = ohneExt + ImageActor.TN + ImageActor.B64 + ext;
    File b64File = new File(dir, newRelName);
    //logger.fine("b64File: " + b64File.getAbsolutePath());
    if(!b64File.exists()) {
      //BildErzeuger be = new BildErzeuger();
      //be.bildErzeugen(dir, newRelName, BildErzeuger.TN, 120, b64File);
      ImageThread it = new ImageThread(dir, newRelName, ImageActor.TN, 120, b64File, datei, ext);
      it.addListener(this);
      if(threadCount < maxThreads) {
        ++threadCount;
        //logger.fine("Thread started, threadCount: " + threadCount);
        it.start();
      } else {
        waitingThreads.add(it);
        //logger.fine("Thread added to wait queue.");
      }
    } else {
      ImageActor be = new ImageActor();
      be.setImgSrc(datei, ext, b64File);
    }
  }
  @Override
  public void finished() {
    --threadCount;
    //logger.fine("Thread finished, threadCound now: " + threadCount);
    if (threadCount < maxThreads) {
      if (waitingThreads.size() > 0) {
        Object o = waitingThreads.get(0);
        if (o instanceof ImageThread) {
          waitingThreads.remove(o);
          ImageThread it = (ImageThread) o;
          ++threadCount;
          //logger.fine("Thread started from wait queue, threadCount now: " + threadCount);
          it.start();
        }
      }
    }
  }
  
  private void emptyListResponse(HttpExchange e) throws IOException {
    HttpResponder r = new HttpResponder();
    String json = "{}";
    logger.log(Level.FINE, "json: ''{0}''", json);
    //logger.log(Level.FINE, "json: ''{0}''", json);
    r.antwortSenden(e, SC_OK, json);        
  }
  private void speichern(HttpExchange exchange, HttpHelper helper) throws IOException {
    String fileName = helper.getFileName(exchange);
    logger.info("fileName: " + fileName);
    // file ist die Datei, um die es geht
    File file = new File(exchange.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fileName);
    String method = exchange.getRequestMethod();
    if (fileName.endsWith(STR_SLASH)) {
      logger.info("neuer Ordner: " + file.getAbsolutePath());
      // neuen Ordner erstellen oder ablehnen, wenn der Ordner schon existiert
      if (method.equalsIgnoreCase(HttpHelper.HTTP_POST)) {
        if (!file.exists()) {
          file.mkdir();
          standardHeaderUndAntwort(exchange, SC_OK, file.getAbsolutePath());
        } else {
          String antwort = "Ordner existiert bereits.";
          standardHeaderUndAntwort(exchange, SC_UNPROCESSABLE_ENTITY, antwort);
        }
      } else {
        String antwort = "PUT fuer neuen Ordner nicht erlaubt, bitte POST verwenden.";
        standardHeaderUndAntwort(exchange, SC_METHOD_NOT_ALLOWED, antwort);
      }
    } else {
      logger.info("Datei speichern: " + file.getAbsolutePath());
      // Datei speichern
      if (method.equalsIgnoreCase(HttpHelper.HTTP_POST)) {
        if (file.exists()) {
          FileTransporter trans = new FileTransporter();
          file = trans.getNewFileName(file);
        }
      } else if (method.equalsIgnoreCase(HttpHelper.HTTP_PUT)) {
        if (file.exists()) {
          /*
            muss delete() sein?
            pruefen: ueberschreibt der FileWriter den alteen Inhalt oder
            entsteht eine unerwuenschte Mischung aus altem und neuem
            Inhalt?
           */
          file.delete();
        } else {
          file.getParentFile().mkdirs();
        }
      }
      // Request Body mit dem Dateiinhalt in einen String lesen
      StringBuilder sb = new StringBuilder();
      InputStream is = exchange.getRequestBody();
      BufferedReader in = new BufferedReader(new InputStreamReader(is));
      String line = in.readLine();
      while (line != null) {
        sb.append(line);
        line = in.readLine();
      }
      // dekodieren
      String content = sb.toString();
      logger.fine(content);
      String decoded = URLDecoder.decode(content, UTF8);
      logger.fine(decoded);
      // in Datei schreiben
      byte[] bytes = decoded.getBytes();
      file.createNewFile();
      OutputStream os = new FileOutputStream(file);
      os.write(bytes);
      os.flush();
      os.close();
      is.close();
      // Antwort senden
      standardHeaderUndAntwort(exchange, SC_OK, file.getAbsolutePath());
    }
  }
  private void copyOrMove(HttpExchange exchange, String quelle, String ziel, int op) throws IOException {
    logger.fine("quelle: " + quelle + ", ziel: " + ziel);
    String[] dateiNamen = dateiliste(exchange);
    copyOrMoveFiles(quelle, ziel, dateiNamen, op, exchange);
    standardHeaderUndAntwort(exchange, SC_OK, "Dateien verarbeitet.");
  }
  private String copyOrMoveFiles(String fromPath, String toPath, String[] fileNames, int operation, HttpExchange e) throws IOException {
    String result = null;
    File srcDir = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fromPath);
    File targetDir = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), toPath);
    for (String fileName : fileNames) {
      File srcFile = new File(srcDir, fileName);
      logger.fine("srcFile: " + srcFile);
      if (srcFile.isDirectory()) {
        logger.fine("srcFile is directory.");
        OrdnerBearbeiter bearbeiter = new OrdnerBearbeiter();
        bearbeiter.setTargetDir(targetDir.toPath());
        bearbeiter.setOperation(operation);
        Files.walkFileTree(srcFile.toPath(), bearbeiter);
      } else {
        Path source = srcFile.toPath();
        File destFile = targetDir.toPath().resolve(source.getFileName()).toFile();
        if (destFile.exists()) {
          FileTransporter trans = new FileTransporter();
          destFile = trans.getNewFileName(destFile);
        }
        if (operation == OP_MOVE) {
          String fname = srcFile.getName().toLowerCase();
          if (fname.endsWith(ImageActor.JPEG)
                  || fname.endsWith(ImageActor.JPG)
                  || fname.endsWith(ImageActor.PNG)) {
            moveImgFilesToDirectory(srcFile, srcDir, targetDir, false);
          } else {
            Files.move(source, destFile.toPath());
          }
        } else {
          Files.copy(source, destFile.toPath());
        }
      }
    }
    return result;
  }
  private void loeschen(HttpExchange exchange, HttpHelper helper) throws IOException {
    String[] dateiNamen = dateiliste(exchange);
    String relPfad = helper.getFileName(exchange);
    deleteFiles(relPfad, Arrays.asList(dateiNamen), exchange);
    standardHeaderUndAntwort(exchange, SC_OK, "Dateien geloescht.");
  }
  private String[] dateiliste(HttpExchange exchange) throws IOException {
    String body = new HttpHelper().bodyLesen(exchange);
    logger.fine("dateien: " + body);
    Gson gson = new Gson();
    return gson.fromJson(body, String[].class);
  }
  public String duplizieren(HttpExchange exchange, HttpHelper helper) throws IOException {
    String relPfad = helper.getFileName(exchange);
    File srcFile = new File(exchange.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), relPfad);
    String fnameext = srcFile.getName();
    int dotpos = fnameext.lastIndexOf(STR_DOT);
    String fname = fnameext.substring(0, dotpos);
    String ext = fnameext.substring(dotpos);
    File srcDir = srcFile.getParentFile();
    File destFile = new File(srcDir, fname + "-Kopie" + ext);
    int i = 1;
    while (destFile.exists()) {
      destFile = new File(srcDir, fname + "-Kopie-" + Integer.toString(++i) + ext);
    }
    Files.copy(srcFile.toPath(), destFile.toPath());
    return destFile.getName();
  }
  public String umbenennen(HttpExchange exchange, HttpHelper helper, String neuerName) throws IOException {
    File neueDatei;
    String relPfad = helper.getFileName(exchange);
    File file = new File(exchange.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), relPfad);
    String fname = file.getName().toLowerCase();
    if(fname.endsWith(ImageActor.JPEG) || fname.endsWith(ImageActor.JPG) || fname.endsWith(ImageActor.PNG)) {
      neueDatei = renameImgFiles(file.getParentFile(), file, neuerName);
    } else {
      neueDatei = new File(file.getParentFile(), neuerName);
      file.renameTo(neueDatei);
    }
    return neueDatei.getName();
  }
  public File renameImgFiles(File targetDir, File targetFile, String newName) throws IOException {
    String alt;
    String neu;
    File neueDatei = targetFile;
    int newdotpos = newName.lastIndexOf(STR_DOT);
    String newfname = newName.substring(0, newdotpos);
    String newext = newName.substring(newdotpos);
    logger.fine("newfname: " + newfname + ", newext: " + newext);
    String fnameext = targetFile.getName();
    int dotpos = fnameext.lastIndexOf(STR_DOT);
    String fname = fnameext.substring(0, dotpos);
    String ext = fnameext.substring(dotpos);
    logger.fine("fname: " + fname + ", ext: " + ext);
    DirectoryStream<Path> stream = Files.newDirectoryStream(targetDir.toPath(), fname + "*" + ext); //"*.{txt,doc,pdf,ppt}"
    for (Path path : stream) {
      logger.fine(path.getFileName().toString());
      alt = path.getFileName().toString();
      logger.fine("alt: " + alt);
      if(alt.contains(ImageActor.TN)) {
        neu = newfname + ImageActor.TN + newext;
      } else if (alt.contains(ImageActor.KL)) {
        neu = newfname + ImageActor.KL + newext;
      } else if(alt.contains(ImageActor.GR)) {
        neu = newfname + ImageActor.GR + newext;
      } else if(alt.contains(ImageActor.MT)) {
        neu = newfname + ImageActor.MT + newext;
      } else if(alt.contains(ImageActor.SM)) {
        neu = newfname + ImageActor.SM + newext;
      } else {
        neu = newName;
      }
      neueDatei = new File(targetDir, neu);
      path.toFile().renameTo(neueDatei);
    }
    stream.close();
    return neueDatei;
  }
  private String deleteFiles(String relPath, List<String> fileNames, HttpExchange e) {
    String result = null;
    try {
      logger.fine(fileNames.toString());
      if (!relPath.startsWith(STR_DOT)) {
        File targetDir = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), relPath); // getTargetDir(relPath);
        logger.fine("targetDir: " + targetDir);
        for (String fileName : fileNames) {
          File targetFile = new File(targetDir, fileName);
          logger.fine(targetFile.getAbsolutePath());
          if (targetFile.isDirectory()) {
            OrdnerBearbeiter bearbeiter = new OrdnerBearbeiter();
            bearbeiter.setOperation(OP_DELETE);
            Files.walkFileTree(targetFile.toPath(), bearbeiter);
          } else {
            /*
                Wenn targetFile mit jpg, jpeg oder png endet,
                muss eine Unterfunktion eine Liste aller Dateien bilden,
                die so heissen, also z.B. alle [Dateiname]*.jpg
             */
            String fname = targetFile.getName().toLowerCase();
            if (fname.endsWith(ImageActor.JPEG)
                    || fname.endsWith(ImageActor.JPG)
                    || fname.endsWith(ImageActor.PNG)) {
              deleteImgFiles(targetDir, targetFile);
            } else {
              targetFile.delete();
            }
          }
        }
        result = "deleted";
      }
    } catch (Throwable ex) {
      logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
    }
    return result;
  }
  private void deleteImgFiles(File targetDir, File targetFile) throws IOException {
    String fnameext = targetFile.getName();
    int dotpos = fnameext.lastIndexOf(STR_DOT);
    String fname = fnameext.substring(0, dotpos);
    String ext = fnameext.substring(dotpos);
    logger.fine("fname: " + fname + ", ext: " + ext);
    DirectoryStream<Path> stream = Files.newDirectoryStream(targetDir.toPath(), fname + "*" + ext); //"*.{txt,doc,pdf,ppt}"
    for (Path path : stream) {
      logger.fine(path.getFileName().toString());
      Files.delete(path);
    }
    stream.close();
  }
  private void moveImgFilesToDirectory(File srcFile, File srcDir, File targetDir, boolean createDestDir) throws IOException {
    String fnameext = srcFile.getName();
    int dotpos = fnameext.lastIndexOf(STR_DOT);
    String fname = fnameext.substring(0, dotpos);
    String ext = fnameext.substring(dotpos);
    logger.fine("fname: " + fname + ", ext: " + ext);
    Path targetPath = targetDir.toPath();
    DirectoryStream<Path> stream = Files.newDirectoryStream(srcDir.toPath(), fname + "*" + ext); //"*.{txt,doc,pdf,ppt}"
    for (Path path : stream) {
      logger.fine(path.getFileName().toString());
      //Files.delete(path);
      Files.move(path, targetPath.resolve(path.getFileName()));
    }
    stream.close();
  }
  private void standardHeaderUndAntwort(HttpExchange exchange, int status, String antwort) throws IOException {
    Headers resHeaders = exchange.getResponseHeaders();
    resHeaders.add(CONTENT_TYPE, HttpHelper.CT_TEXT_HTML);
    new HttpResponder().antwortSenden(exchange, status, antwort);
  }
  /* --------- ZIP entpacken ---------------- */
  public String extractZipfile(String fName, String relPath, HttpExchange e) {
    logger.fine("fName: " + fName + ", relPath: " + relPath);
    String result = null;
    if (!relPath.startsWith(".")) {
      try {
        //File targetDir = new File(fileBase, relPath);
        //File targetDir = getTargetDir(relPath);
        File archive = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fName);
        if(extract(archive)) {
          result = "ok";
        } else {
          result = "error while extracting";
        }
      } catch(Exception ex) {
        result = ex.getLocalizedMessage();
        logger.log(Level.SEVERE, ex.getLocalizedMessage(), ex);
      }
    } else {
      result = "Falsche relative Pfadangabe.";
    }
    return result;
  }
  /**
    * extract a given ZIP archive to the folder respective archive resides in
    * @param archive  the archive to extract
    * @throws Exception
    */
   private boolean extract(File archive) throws Exception {
      ZipFile zipfile = new ZipFile(archive);
      Enumeration en = zipfile.entries();
      while(en.hasMoreElements()) {
         ZipEntry zipentry = (ZipEntry) en.nextElement();
         unzip(zipfile, zipentry, archive.getParent());
      }
      zipfile.close();
      return true;
   }
   /**
    * unzip a given entry of a given zip file to a given location
    * @param zipfile  the zip file to read an entry from
    * @param zipentry  the zip entry to read
    * @param destPath  the path to the destination location for the extracted content
    * @throws IOException
    */
   private void unzip(ZipFile zipfile, ZipEntry zipentry, String destPath) throws IOException {
      byte buf[] = new byte[1024];
      InputStream is = zipfile.getInputStream(zipentry);
      String outFileName = destPath + File.separator + zipentry.getName();
      File file = new File(outFileName);
      if(!zipentry.isDirectory()) {
         file.getParentFile().mkdirs();
         if(!file.exists())
            file.createNewFile();
         FileOutputStream fos = new FileOutputStream(file);
         int i = is.read(buf, 0, 1024);
         while(i > -1) {
            fos.write(buf, 0, i);
            i = is.read(buf, 0, 1024);
         }
         fos.close();
         is.close();
      } else {
         file.mkdirs();
      }
   }
  /* --------------- Ordner packen ----------------- */
  /**
   * Einen Ordner packen.
   *
   * Als Ziel wird eine neue Datei mit Dateiendung '.zip' erzeugt, die so
   * heisst wie der Ordner, der gapckt werden soll. Die Datei mit
   * dem gepackten Ordnerinhalt wird in dem Ordner angelegt, der den zu
   * packenden Ordner enthaelt.
   *
   * @param fName  Name des zu packenden Ordners
   * @param relPath  relativer Pfad zum Ordner, der gepackt werden soll
   * @return die Meldung mit dem Ergebnis. Wenn die Meldung nicht "ok" lautet
   * wurde die ZIP-Datei nicht erzeugt und die Meldung nennt den Grund.
   */
  public String packFolder(String fName, String relPath, HttpExchange e) {
    if (!relPath.startsWith(".")) {
      try {
        //String fName = getFileName(e);
        logger.fine("fName: " + fName);
        if (fName.endsWith(STR_SLASH)) {
          File dir = new File(e.getHttpContext().getAttributes().get(FileHandler.ATTR_FILE_BASE).toString(), fName);
          if(dir.isDirectory()) {
            logger.fine("absPath: " + dir.getAbsolutePath());
            File parentDir = dir.getParentFile();
            StringBuilder fname = new StringBuilder();
            fname.append(dir.getName());
            fname.append(".zip");
            File archiveFile = new File(parentDir, fname.toString());
            pack(dir.getAbsolutePath(), archiveFile.getAbsolutePath());
            return "ok";
          } else {
            return "kein Ordner";
          }
        } else {
          return "kein Ordner";
        }
      } catch(Exception ex) {
        String result = ex.getLocalizedMessage();
        logger.log(Level.SEVERE, result, ex);
        return result;
      }
    } else {
      return "Falsche relative Pfadangabe";
    }
  }
   /**
    * pack the contents of a given folder into a new ZIP compressed archive
    * @param folder  absolute path and name of the folder to pack
    * @param archive  absolute path and name of the archive to create from the given files
    * @throws Exception
    */
   private boolean pack(String folder, String archive) throws Exception {
      File file = new File(archive);
      FileOutputStream fos = new FileOutputStream(file);
      CheckedOutputStream checksum = new CheckedOutputStream(fos, new Adler32());
      ZipOutputStream zos = new ZipOutputStream(checksum);
      pack(zos, folder, "");
      zos.flush();
      zos.finish();
      zos.close();
      fos.flush();
      fos.close();
      return true;
   }
   /**
    * go through the given file structure recursively
    * @param zipFile  the ZIP file to write to
    * @param srcDir  the directory to pack during this cycle
    * @param subDir  the subdirectory to append to names of file entries inside the archive
    * @throws IOException
    */
   private void pack(ZipOutputStream zipFile, String srcDir, String subDir) throws IOException {
      File[] files = new File(srcDir).listFiles();
      for(int i = 0; i < files.length; i++) {
         if(files[i].isDirectory()) {
            pack(zipFile, files[i].getAbsolutePath(), subDir + File.separator + files[i].getName());
         }
         else {
            packFile(zipFile, subDir, files[i]);
         }
      }
   }
   /**
    * pack a given file
    * @param zipFile  the ZIP archive to pack to
    * @param dir  the directory name to append to name of file entry inside archive
    * @param file  the file to pack
    * @throws IOException
    */
   private void packFile(ZipOutputStream zipFile, String dir, File file) throws IOException
   {
      FileInputStream fileinputstream = new FileInputStream(file);
      byte buf[] = new byte[fileinputstream.available()];
      fileinputstream.read(buf);
      String dirWithSlashes = dir.replace('\\', '/');
      //System.out.println("zipping " + dirWithSlashes + "/" + file.getName());
      ZipEntry ze = new ZipEntry(dirWithSlashes + "/" + file.getName());
      ze.setMethod(ZipEntry.DEFLATED);
      zipFile.putNextEntry(ze);
      zipFile.write(buf, 0, buf.length);
      zipFile.closeEntry();
      fileinputstream.close();
   }
}