1 | ![WebDAV](https://raw.githubusercontent.com/perry-mitchell/webdav-client/master/webdav.jpg)
|
2 |
|
3 | > A WebDAV client written in JavaScript for NodeJS and the browser.
|
4 |
|
5 | [![Build Status](https://travis-ci.org/perry-mitchell/webdav-client.svg?branch=master)](https://travis-ci.org/perry-mitchell/webdav-client) [![npm version](https://badge.fury.io/js/webdav.svg)](https://www.npmjs.com/package/webdav) [![monthly downloads](https://img.shields.io/npm/dm/webdav.svg)](https://www.npmjs.com/package/webdav) [![total downloads](https://img.shields.io/npm/dt/webdav.svg?label=total%20downloads)](https://www.npmjs.com/package/webdav)
|
6 |
|
7 | ## About
|
8 |
|
9 | WebDAV is a well-known, stable and highly flexible protocol for interacting with remote filesystems via an API. Being that it is so widespread, many file hosting services such as **Box**, **ownCloud**/**Nextcloud** and **Yandex** use it as a fallback to their other interfaces.
|
10 |
|
11 | This library provides a **WebDAV client** interface that makes interacting with WebDAV enabled services easy. The API returns promises and resolve with the results. It parses and prepares directory-contents requests for easy consumption, as well as providing methods for fetching things like file stats and quotas.
|
12 |
|
13 | Please read the [contribution guide](CONTRIBUTING.md) if you plan on making an issue or PR.
|
14 |
|
15 | ### Node support
|
16 |
|
17 | This library is compatibale with **NodeJS version 10** and above (For version 6/8 support, use versions in the range of `2.*`. For version 4 support, use versions in the range of `1.*`). Version 2.x is now in maintenance mode and will receive no further feature additions. It will receive the odd bug fix when necessary. Version 1.x is no longer supported.
|
18 |
|
19 | ### Usage in the Browser
|
20 |
|
21 | As of version 3, WebDAV client is now supported in the browser. The compilation settings specify a minimum supported browser version of Internet Explorer 11, however testing in this browser is not performed regularly.
|
22 |
|
23 | _Although you may choose to transpile this library's default entry point (NodeJS) yourself, it is not advised - use the dedicated web version instead._
|
24 |
|
25 | You can use the web version via a different entry point:
|
26 |
|
27 | ```javascript
|
28 | import { createClient } from "webdav/web";
|
29 | ```
|
30 |
|
31 | The browser version uses a UMD-style module definition, meaning you can simply load the library within your browser using a `<script>` tag. When using this method the library is made available on the window object as such: `window.WebDAV`. For example:
|
32 |
|
33 | ```javascript
|
34 | const client = window.WebDAV.createClient(/* ... */);
|
35 | ```
|
36 |
|
37 | **NB:** Streams are not available within the browser, so `createReadStream` and `createWriteStream` are just stubbed. Calling them will throw an exception.
|
38 |
|
39 | ## Installation
|
40 |
|
41 | Simple install as a dependency using npm:
|
42 |
|
43 | ```
|
44 | npm install webdav --save
|
45 | ```
|
46 |
|
47 | ## Usage
|
48 |
|
49 | Usage entails creating a client adapter instance by calling the factory function `createClient`:
|
50 |
|
51 | ```javascript
|
52 | const { createClient } = require("webdav");
|
53 |
|
54 | const client = createClient(
|
55 | "https://webdav.example.com/marie123",
|
56 | {
|
57 | username: "marie",
|
58 | password: "myS3curePa$$w0rd"
|
59 | }
|
60 | );
|
61 |
|
62 | // Get directory contents
|
63 | const directoryItems = await client.getDirectoryContents("/");
|
64 | // Outputs a structure like:
|
65 | // [{
|
66 | // filename: "/my-file.txt",
|
67 | // basename: "my-file.txt",
|
68 | // lastmod: "Mon, 10 Oct 2018 23:24:11 GMT",
|
69 | // size: 371,
|
70 | // type: "file"
|
71 | // }]
|
72 | ```
|
73 |
|
74 | Make sure to read the [API documentation](API.md) for more information on the [available adapter methods](API.md#ClientInterface).
|
75 |
|
76 | ### Authentication & Connection
|
77 |
|
78 | `webdav` uses `Basic` authentication by default, if `username` and `password` are provided (if none are provided, no `Authorization` header is specified). It also supports OAuth tokens and Digest auth.
|
79 |
|
80 | #### Basic or no authentication
|
81 |
|
82 | You can use the client without authentication if the server doesn't require it - simply avoid passing any values to `username`, `password`, `token` or `digest` in the config.
|
83 |
|
84 | To use basic authentication, simply pass a `username` and `password` in the config.
|
85 |
|
86 | `webdav` also allows for overriding the built in HTTP and HTTPS agents by setting the properties `httpAgent` & `httpsAgent` accordingly. These should be instances of node's [http.Agent](https://nodejs.org/api/http.html#http_class_http_agent) and [https.Agent](https://nodejs.org/api/https.html#https_class_https_agent) respectively.
|
87 |
|
88 | #### OAuth tokens
|
89 |
|
90 | To use a token to authenticate, simply pass the token data to the `token` field:
|
91 |
|
92 | ```javascript
|
93 | createClient(
|
94 | "https://address.com",
|
95 | {
|
96 | token: {
|
97 | "access_token": "2YotnFZFEjr1zCsicMWpAA",
|
98 | "token_type": "example",
|
99 | "expires_in": 3600,
|
100 | "refresh_token": "tGzv3JOkF0XG5Qx2TlKWIA",
|
101 | "example_parameter": "example_value"
|
102 | }
|
103 | }
|
104 | );
|
105 | ```
|
106 |
|
107 | #### Digest authentication
|
108 |
|
109 | If a server requires digest-based authentication, you can enable this functionality by setting `digest` to true:
|
110 |
|
111 | ```javascript
|
112 | createClient(
|
113 | "https://address.com",
|
114 | {
|
115 | username: "someUser",
|
116 | password: "myS3curePa$$w0rd",
|
117 | digest: true
|
118 | }
|
119 | );
|
120 | ```
|
121 |
|
122 | ### Methods
|
123 |
|
124 | #### copyFile
|
125 |
|
126 | Copy a file from one remote location to another:
|
127 |
|
128 | ```javascript
|
129 | await client.copyFile("/sub/item.txt", "/destination/item.txt");
|
130 | ```
|
131 |
|
132 | #### createDirectory
|
133 |
|
134 | Create a new directory:
|
135 |
|
136 | ```javascript
|
137 | await client.createDirectory("/completely/new/path");
|
138 | ```
|
139 |
|
140 | #### createReadStream
|
141 |
|
142 | Create a read stream targeted at a remote file:
|
143 |
|
144 | ```javascript
|
145 | client
|
146 | .createReadStream("/video.mp4")
|
147 | .pipe(fs.createWriteStream("~/video.np4"));
|
148 | ```
|
149 |
|
150 | #### createWriteStream
|
151 |
|
152 | Create a write stream targeted at a remote file:
|
153 |
|
154 | ```javascript
|
155 | fs.createReadStream("~/Music/song.mp3")
|
156 | .pipe(client.createWriteStream("/music/song.mp3"));
|
157 | ```
|
158 |
|
159 | #### deleteFile
|
160 |
|
161 | Delete a remote file:
|
162 |
|
163 | ```javascript
|
164 | await client.deleteFile("/tmp.dat");
|
165 | ```
|
166 |
|
167 | ### exists
|
168 |
|
169 | Check if a file or directory exists:
|
170 |
|
171 | ```javascript
|
172 | if (await client.exists("/some/path") === false) {
|
173 | await client.createDirectory("/some/path");
|
174 | }
|
175 | ```
|
176 |
|
177 | #### getDirectoryContents
|
178 |
|
179 | Get the contents of a remote directory. Returns an array of [item stats](#item-stat).
|
180 |
|
181 | ```javascript
|
182 | // Get current directory contents:
|
183 | const contents = await client.getDirectoryContents("/");
|
184 | // Get all contents:
|
185 | const contents = await client.getDirectoryContents("/", { deep: true });
|
186 | ```
|
187 |
|
188 | Files can be globbed using the `glob` option (processed using [`minimatch`](https://github.com/isaacs/minimatch)). When using a glob pattern it is recommended to fetch `deep` contents:
|
189 |
|
190 | ```javascript
|
191 | const images = await client.getDirectoryContents("/", { deep: true, glob: "/**/*.{png,jpg,gif}" });
|
192 | ```
|
193 |
|
194 | #### getFileContents
|
195 |
|
196 | Fetch the contents of a remote file. Binary contents are returned by default (Buffer):
|
197 |
|
198 | ```javascript
|
199 | const buff = await client.getFileContents("/package.zip");
|
200 | ```
|
201 |
|
202 | It is recommended to use streams if the files being transferred are large.
|
203 |
|
204 | Text files can also be fetched:
|
205 |
|
206 | ```javascript
|
207 | const str = await client.getFileContents("/config.json", { format: "text" });
|
208 | ```
|
209 |
|
210 | #### getFileDownloadLink
|
211 |
|
212 | Return a public link where a file can be downloaded. **This exposes authentication details in the URL**.
|
213 |
|
214 | ```javascript
|
215 | const downloadLink = client.getFileDownloadLink("/image.png");
|
216 | ```
|
217 |
|
218 | _Not all servers may support this feature. Only Basic authentication and unauthenticated connections support this method._
|
219 |
|
220 | #### getFileUploadLink
|
221 |
|
222 | Return a URL for a file upload:
|
223 |
|
224 | ```javascript
|
225 | const uploadLink = client.getFileUploadLink("/image.png");
|
226 | ```
|
227 |
|
228 | _See `getFileDownloadLink` for support details._
|
229 |
|
230 | #### getQuota
|
231 |
|
232 | Get the quota information for the current account:
|
233 |
|
234 | ```javascript
|
235 | const quota = await client.getQuota();
|
236 | // {
|
237 | // "used": 1938743,
|
238 | // "available": "unlimited"
|
239 | // }
|
240 | ```
|
241 |
|
242 | #### moveFile
|
243 |
|
244 | Move a remote file to another remote location:
|
245 |
|
246 | ```javascript
|
247 | await client.moveFile("/file1.png", "/file2.png");
|
248 | ```
|
249 |
|
250 | #### putFileContents
|
251 |
|
252 | Write data to a remote file:
|
253 |
|
254 | ```javascript
|
255 | // Write a buffer:
|
256 | await client.putFileContents("/my/file.jpg", imageBuffer, { overwrite: false });
|
257 | // Write a text file:
|
258 | await client.putFileContents("/my/file.txt", str);
|
259 | ```
|
260 |
|
261 | Handling Upload Progress (browsers only):
|
262 | *This uses the axios onUploadProgress callback which uses the native XMLHttpRequest [progress event](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequestEventTarget/onprogress).*
|
263 |
|
264 | ```javascript
|
265 | // Upload a file and log the progress to the console:
|
266 | await client.putFileContents("/my/file.jpg", imageFile, { onUploadProgress: progress => {
|
267 | console.log(`Uploaded ${progress.loaded} bytes of ${progress.total}`);
|
268 | } });
|
269 | ```
|
270 |
|
271 | #### stat
|
272 |
|
273 | Get a file or directory stat object:
|
274 |
|
275 | ```javascript
|
276 | const stat = await client.stat("/some/file.tar.gz");
|
277 | ```
|
278 |
|
279 | Returns an [item stat](#item-stat).
|
280 |
|
281 | ### Custom requests
|
282 |
|
283 | Custom requests can be made to the attached host:
|
284 |
|
285 | ```javascript
|
286 | const contents = await client.customRequest("/alrighty.jpg", {
|
287 | method: "PROPFIND",
|
288 | headers: {
|
289 | Accept: "text/plain",
|
290 | Depth: 0
|
291 | },
|
292 | responseType: "text"
|
293 | });
|
294 | ```
|
295 |
|
296 | ### Returned data structures
|
297 |
|
298 | #### Directory contents items
|
299 |
|
300 | Each item returned by `getDirectoryContents` is basically an [item stat](#item-stat). If the `details: true` option is set, each item stat (as mentioned in the stat documentation) will also include the `props` property containing extra properties returned by the server. No particular property in `props`, not its format or value, is guaranteed.
|
301 |
|
302 | You can request all files in the file-tree (infinite depth) by calling `getDirectoryContents` with the option `deep: true`. All items will be returned in a flat array, where the `filename` will hold the absolute path.
|
303 |
|
304 | #### Detailed responses
|
305 |
|
306 | Requests that return results, such as `getDirectoryContents`, `getFileContents`, `getQuota` and `stat`, can be configured to return more detailed information, such as response headers. Pass `{ details: true }` to their options argument to receive an object like the following:
|
307 |
|
308 | | Property | Type | Description |
|
309 | |--------------|-----------------|----------------------------------------|
|
310 | | data | * | The data returned by the procedure. Will be whatever type is returned by calling without `{ details: true }` |
|
311 | | headers | Object | The response headers. |
|
312 |
|
313 | #### Item stat
|
314 |
|
315 | Item stats are objects with properties that descibe a file or directory. They resemble the following:
|
316 |
|
317 | ```json
|
318 | {
|
319 | "filename": "/test",
|
320 | "basename": "test",
|
321 | "lastmod": "Tue, 05 Apr 2016 14:39:18 GMT",
|
322 | "size": 0,
|
323 | "type": "directory",
|
324 | "etag": null
|
325 | }
|
326 | ```
|
327 |
|
328 | or:
|
329 |
|
330 | ```json
|
331 | {
|
332 | "filename": "/image.jpg",
|
333 | "basename": "image.jpg",
|
334 | "lastmod": "Sun, 13 Mar 2016 04:23:32 GMT",
|
335 | "size": 42497,
|
336 | "type": "file",
|
337 | "mime": "image/jpeg",
|
338 | "etag": "33a728c7f288ede1fecc90ac6a10e062"
|
339 | }
|
340 | ```
|
341 |
|
342 | Properties:
|
343 |
|
344 | | Property name | Type | Present | Description |
|
345 | |---------------|---------|--------------|---------------------------------------------|
|
346 | | filename | String | Always | File path of the remote item |
|
347 | | basename | String | Always | Base filename of the remote item, no path |
|
348 | | lastmod | String | Always | Last modification date of the item |
|
349 | | size | Number | Always | File size - 0 for directories |
|
350 | | type | String | Always | Item type - "file" or "directory" |
|
351 | | mime | String | Files only | Mime type - for file items only |
|
352 | | etag | String / null | When supported | [ETag](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag) of the file |
|
353 | | props | Object | `details: true` | Props object containing all item properties returned by the server |
|
354 |
|
355 | ## Compatibility
|
356 | This library has been tested to work with the following WebDAV servers or applications:
|
357 |
|
358 | * [ownCloud](https://owncloud.org/) ¹
|
359 | * [Nextcloud](https://nextcloud.com/) ¹
|
360 | * [Yandex.ru](https://yandex.ru/)
|
361 | * [jsDAV](https://github.com/mikedeboer/jsDAV)
|
362 | * [webdav-server](https://github.com/OpenMarshal/npm-WebDAV-Server)
|
363 |
|
364 | ¹ These services will work if CORS is correctly configured to return the proper headers. This may not work by default.
|
365 |
|
366 | ### CORS
|
367 | CORS is a security enforcement technique employed by browsers to ensure requests are executed to and from expected contexts. It can conflict with this library if the target server doesn't return CORS headers when making requests from a browser. It is your responsibility to handle this.
|
368 |
|
369 | It is a known issue that ownCloud and Nextcloud servers by default don't return friendly CORS headers, making working with this library within a browser context impossible. You can of course force the addition of CORS headers (Apache or Nginx configs) yourself, but do this at your own risk.
|