package com.kunfei.bookshelf.utils.web_dav; import com.kunfei.bookshelf.utils.web_dav.http.Handler; import com.kunfei.bookshelf.utils.web_dav.http.HttpAuth; import com.kunfei.bookshelf.utils.web_dav.http.OkHttp; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import okhttp3.Credentials; import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; public class WebDavFile { public final static String TAG = WebDavFile.class.getSimpleName(); public final static String OBJECT_NOT_EXISTS_TAG = "ObjectNotFound"; // 指定返回哪些属性 private final static String DIR = "<?xml version=\"1.0\"?>\n" + "<a:propfind xmlns:a=\"DAV:\">\n" + "<a:prop>\n" + "<a:displayname/>\n<a:resourcetype/>\n<a:getcontentlength/>\n<a:creationdate/>\n<a:getlastmodified/>\n%s" + "</a:prop>\n" + "</a:propfind>"; private URL url; private String httpUrl; private String displayName; private long createTime; private long lastModified; private long size; private boolean isDirectory = true; private boolean exists = false; private String parent = ""; private String urlName = ""; private OkHttpClient okHttpClient; public WebDavFile(String url) throws MalformedURLException { this.url = new URL(null, url, Handler.HANDLER); okHttpClient = OkHttp.getInstance().client(); } public String getUrl() { if (httpUrl == null) { String raw = url.toString().replace("davs://", "https://").replace("dav://", "http://"); try { httpUrl = URLEncoder.encode(raw, "UTF-8") .replaceAll("\\+", "%20") .replaceAll("%3A", ":") .replaceAll("%2F", "/"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return httpUrl; } public String getPath() { return url.toString(); } /** * 填充文件信息。实例化WebDAVFile对象时,并没有将远程文件的信息填充到实例中。需要手动填充! * * @return 远程文件是否存在 */ public boolean indexFileInfo() throws IOException { Response response = propFindResponse(new ArrayList<>()); String s = ""; try { if (response == null || !response.isSuccessful()) { this.exists = false; return false; } s = response.body().string(); } catch (Exception e) { e.printStackTrace(); } return false; } /** * 列出当前路径下的文件。默认列出文件的如下属性:displayname、resourcetype、getcontentlength、creationdate、getlastmodified * * @return 文件列表 */ public List<WebDavFile> listFiles() throws IOException { return listFiles(new ArrayList<>()); } /** * 列出当前路径下的文件 * * @param propsList 指定列出文件的哪些属性 * @return 文件列表 */ public List<WebDavFile> listFiles(ArrayList<String> propsList) throws IOException { Response response = propFindResponse(propsList); try { assert response != null; if (response.isSuccessful()) { return parseDir(response.body().string()); } } catch (Exception e) { e.printStackTrace(); } return new ArrayList<>(); } private Response propFindResponse(ArrayList<String> propsList) throws IOException { return propFindResponse(propsList, 1); } private Response propFindResponse(ArrayList<String> propsList, int depth) throws IOException { StringBuilder requestProps = new StringBuilder(); for (String p : propsList) { requestProps.append("<a:").append(p).append("/>\n"); } String requestPropsStr; if (requestProps.toString().isEmpty()) { requestPropsStr = DIR.replace("%s", ""); } else { requestPropsStr = String.format(DIR, requestProps.toString() + "\n"); } Request.Builder request = new Request.Builder() .url(getUrl()) // 添加RequestBody对象,可以只返回的属性。如果设为null,则会返回全部属性 // 注意:尽量手动指定需要返回的属性。若返回全部属性,可能后由于Prop.java里没有该属性名,而崩溃。 .method("PROPFIND", RequestBody.create(MediaType.parse("text/plain"), requestPropsStr)); HttpAuth.Auth auth = HttpAuth.getAuth(); if (auth != null) { request.header("Authorization", Credentials.basic(auth.getUser(), auth.getPass())); } request.header("Depth", depth < 0 ? "infinity" : Integer.toString(depth)); return okHttpClient.newCall(request.build()).execute(); } private List<WebDavFile> parseDir(String s) { List<WebDavFile> list = new ArrayList<>(); Document document = Jsoup.parse(s); Elements elements = document.getElementsByTag("d:response"); String baseUrl = getUrl().endsWith("/") ? getUrl() : getUrl() + "/"; for (Element element : elements) { String href = element.getElementsByTag("d:href").get(0).text(); if (!href.endsWith("/")) { String fileName = href.substring(href.lastIndexOf("/") + 1); WebDavFile webDavFile; try { webDavFile = new WebDavFile(baseUrl + fileName); webDavFile.setDisplayName(fileName); webDavFile.setUrlName(href); list.add(webDavFile); } catch (MalformedURLException e) { e.printStackTrace(); } } } return list; } public InputStream getInputStream() { Request.Builder request = new Request.Builder() .url(getUrl()); HttpAuth.Auth auth = HttpAuth.getAuth(); if (auth != null) { request.header("Authorization", Credentials.basic(auth.getUser(), auth.getPass())); } try { Response response = okHttpClient.newCall(request.build()).execute(); return response.body().byteStream(); } catch (IOException | IllegalArgumentException e) { e.printStackTrace(); } return null; } /** * 根据自己的URL,在远程处创建对应的文件夹 * * @return 是否创建成功 */ public boolean makeAsDir() throws IOException { Request.Builder request = new Request.Builder() .url(getUrl()) .method("MKCOL", null); return execRequest(request); } /** * 下载到本地 * * @param savedPath 本地的完整路径,包括最后的文件名 * @param replaceExisting 是否替换本地的同名文件 * @return 下载是否成功 */ public boolean download(String savedPath, boolean replaceExisting) { File file = new File(savedPath); if (file.exists()) { if (replaceExisting) { file.delete(); } else { return false; } } InputStream in = getInputStream(); FileOutputStream out = null; try { file.createNewFile(); out = new FileOutputStream(file); byte[] buffer = new byte[1024 * 8]; int byteRead; while ((byteRead = in.read(buffer)) != -1) { out.write(buffer, 0, byteRead); } out.flush(); return true; } catch (Exception e) { e.printStackTrace(); } finally { try { if (out != null) out.close(); } catch (Exception e) { e.printStackTrace(); } } return false; } /** * 上传文件 * * @param localPath 本地文件路径 * @return 是否成功成功 */ public boolean upload(String localPath) throws IOException { return upload(localPath, null); } public boolean upload(String localPath, String contentType) throws IOException { File file = new File((localPath)); if (!file.exists()) return false; MediaType mediaType = contentType == null ? null : MediaType.parse(contentType); // 务必注意RequestBody不要嵌套,不然上传时内容可能会被追加多余的文件信息 RequestBody fileBody = RequestBody.create(mediaType, file); Request.Builder request = new Request.Builder() .url(getUrl()) .put(fileBody); return execRequest(request); } /** * 执行请求,获取响应结果 * * @param requestBuilder 因为还需要追加验证信息,所以此处传递Request.Builder的对象,而不是Request的对象 * @return 请求执行的结果 */ private boolean execRequest(Request.Builder requestBuilder) throws IOException { HttpAuth.Auth auth = HttpAuth.getAuth(); if (auth != null) { requestBuilder.header("Authorization", Credentials.basic(auth.getUser(), auth.getPass())); } Response response = okHttpClient.newCall(requestBuilder.build()).execute(); return response.isSuccessful(); } /** * 打印对象内的所有属性 */ public static <T> T printAllAttrs(String className, Object o) { try { Class<?> c = Class.forName(className); Field[] fields = c.getDeclaredFields(); for (Field f : fields) { f.setAccessible(true); } System.out.println("=============" + className + "==============="); for (Field f : fields) { String field = f.toString().substring(f.toString().lastIndexOf(".") + 1); //取出属性名称 System.out.println(field + " --> " + f.get(o)); } } catch (Exception e) { e.printStackTrace(); } return null; } public String getDisplayName() { return displayName; } public void setDisplayName(String displayName) { this.displayName = displayName; } public long getCreateTime() { return createTime; } public void setCreateTime(long createTime) { this.createTime = createTime; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } public long getSize() { return size; } public void setSize(long size) { this.size = size; } public void setUrlName(String urlName) { this.urlName = urlName; } public boolean isDirectory() { return isDirectory; } public String getURLName() { if (urlName.isEmpty()) { urlName = (parent.isEmpty() ? url.getFile() : url.toString().replace(parent, "")). replace("/", ""); } return urlName; } public String getHost() { return url.getHost(); } public boolean canRead() { return true; } public boolean canWrite() { return false; } public boolean exists() { return exists; } public void exists(boolean exists) { this.exists = exists; } public String getParent() { return parent; } public void setParent(String path) { parent = path; } public void setIsDirectory(boolean isDirectory) { this.isDirectory = isDirectory; } }