One of the more challenging tasks on the client side is working with SharePoint files, especially if they are large files. We have added some methods to the library to help and their use is outlined below.
Reading files from the client using REST is covered in the below examples. The important thing to remember is choosing which format you want the file in so you can appropriately process it. You can retrieve a file as Blob, Buffer, JSON, or Text. If you have a special requirement you could also write your own parser.
import { sp } from "@pnp/sp";
sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.avi").getBlob().then((blob: Blob) => {});
sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.avi").getBuffer().then((buffer: ArrayBuffer) => {});
sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.json").getJSON().then((json: any) => {});
sp.web.getFileByServerRelativeUrl("/sites/dev/documents/file.txt").getText().then((text: string) => {});
// all of these also work from a file object no matter how you access it
sp.web.getFolderByServerRelativeUrl("/sites/dev/documents").files.getByName("file.txt").getText().then((text: string) => {});
Likewise you can add files using one of two methods, add or addChunked. The second is appropriate for larger files, generally larger than 10 MB but this may differ based on your bandwidth/latency so you can adjust the code to use the chunked method. The below example shows getting the file object from an input and uploading it to SharePoint, choosing the upload method based on file size.
declare var require: (s: string) => any;
import { ConsoleListener, Web, Logger, LogLevel, ODataRaw } from "@pnp/sp";
import { auth } from "./auth";
let $ = require("jquery");
let siteUrl = "https://mytenant.sharepoint.com/sites/dev";
// comment this out for non-node execution
// auth(siteUrl);
Logger.subscribe(new ConsoleListener());
Logger.activeLogLevel = LogLevel.Verbose;
let web = new Web(siteUrl);
$(() => {
$("#testingdiv").append("<button id='thebuttontodoit'>Do It</button>");
$("#thebuttontodoit").on('click', (e) => {
e.preventDefault();
let input = <HTMLInputElement>document.getElementById("thefileinput");
let file = input.files[0];
// you can adjust this number to control what size files are uploaded in chunks
if (file.size <= 10485760) {
// small upload
web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.add(file.name, file, true).then(_ => Logger.write("done"));
} else {
// large upload
web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.addChunked(file.name, file, data => {
Logger.log({ data: data, level: LogLevel.Verbose, message: "progress" });
}, true).then(_ => Logger.write("done!"));
}
});
});
You can also update the file properties of a newly uploaded file using code similar to the below snippet:
import { sp } from "@pnp/sp";
sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared%20Documents/test/").files.add(file.name, file, true).then(f => {
f.file.getItem().then(item => {
item.update({
Title: "A Title",
OtherField: "My Other Value"
});
});
});
You can of course use similar methods to update existing files as shown below:
import { sp } from "@pnp/sp";
sp.web.getFileByServerRelativeUrl("/sites/dev/documents/test.txt").setContent("New string content for the file.");
sp.web.getFileByServerRelativeUrl("/sites/dev/documents/test.mp4").setContentChunked(file);
The library provides helper methods for checking in, checking out, and approving files. Examples of these methods are shown below.
Check in takes two optional arguments, comment and check in type.
import { sp, CheckinType } from "@pnp/sp";
// default options with empty comment and CheckinType.Major
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin().then(_ => {
console.log("File checked in!");
});
// supply a comment (< 1024 chars) and using default check in type CheckinType.Major
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin("A comment").then(_ => {
console.log("File checked in!");
});
// Supply both comment and check in type
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkin("A comment", CheckinType.Overwrite).then(_ => {
console.log("File checked in!");
});
Check out takes no arguments.
import { sp } from "@pnp/sp";
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").checkout().then(_ => {
console.log("File checked out!");
});
You can also approve or deny files in libraries that use approval. Approve takes a single required argument of comment, the comment is optional for deny.
import { sp } from "@pnp/sp";
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").approve("Approval Comment").then(_ => {
console.log("File approved!");
});
// deny with no comment
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").deny().then(_ => {
console.log("File denied!");
});
// deny with a supplied comment.
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").deny("Deny comment").then(_ => {
console.log("File denied!");
});
You can both publish and unpublish a file using the library. Both methods take an optional comment argument.
import { sp } from "@pnp/sp";
// publish with no comment
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").publish().then(_ => {
console.log("File published!");
});
// publish with a supplied comment.
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").publish("Publish comment").then(_ => {
console.log("File published!");
});
// unpublish with no comment
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").unpublish().then(_ => {
console.log("File unpublished!");
});
// unpublish with a supplied comment.
sp.web.getFileByServerRelativeUrl("/sites/dev/shared documents/file.txt").unpublish("Unpublish comment").then(_ => {
console.log("File unpublished!");
});
Both the addChunked and setContentChunked methods support options beyond just supplying the file content.
A method that is called each time a chunk is uploaded and provides enough information to report progress or update a progress bar easily. The method has the signature:
(data: ChunkedFileUploadProgressData) => void
The data interface is:
export interface ChunkedFileUploadProgressData {
stage: "starting" | "continue" | "finishing";
blockNumber: number;
totalBlocks: number;
chunkSize: number;
currentPointer: number;
fileSize: number;
}
This property controls the size of the individual chunks and is defaulted to 10485760 bytes (10 MB). You can adjust this based on your bandwidth needs - especially if writing code for mobile uploads or you are seeing frequent timeouts.
This method allows you to get the item associated with this file. You can optionally specify one or more select fields. The result will be merged with a new Item instance so you will have both the returned property values and chaining ability in a single object.
import { sp } from "@pnp/sp";
sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem().then(item => {
console.log(item);
});
sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem("Title", "Modified").then(item => {
console.log(item);
});
sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem().then(item => {
// you can also chain directly off this item instance
item.getCurrentUserEffectivePermissions().then(perms => {
console.log(perms);
});
});
You can also supply a generic typing parameter and the resulting type will be a union type of Item and the generic type parameter. This allows you to have proper intellisense and type checking.
import { sp } from "@pnp/sp";
// also supports typing the objects so your type will be a union type
sp.web.getFolderByServerRelativeUrl("/sites/dev/Shared Documents/test").getItem<{ Id: number, Title: string }>("Id", "Title").then(item => {
// You get intellisense and proper typing of the returned object
console.log(`Id: ${item.Id} -- ${item.Title}`);
// You can also chain directly off this item instance
item.getCurrentUserEffectivePermissions().then(perms => {
console.log(perms);
});
});