Skip to content

Instantly share code, notes, and snippets.

@princeppy
Last active April 5, 2019 13:56
Show Gist options
  • Save princeppy/8df1ecac9df01c3023df4a2a13d00a20 to your computer and use it in GitHub Desktop.
Save princeppy/8df1ecac9df01c3023df4a2a13d00a20 to your computer and use it in GitHub Desktop.

Migrate SharePoint List Item Attachments from SharePoint Online list to another Site

import React, { Component } from "react";
// import PropTypes from "prop-types";

import Axios from "axios";

const axiosG = Axios.create({
  baseURL: "http://localhost:3090",
  headers: { Accept: "application/json;odata=nometadata" }
});

const axiosP = Axios.create({
  baseURL: "http://localhost:3090",
  headers: {
    Accept: "application/json;odata=verbose",
    "Content-Type": "application/json;odata=verbose"
  }
});

const getSourceLists = listName =>
  axiosG
    .get(
      `/sites/ModernRootSite/IT/_api/web/lists/getbytitle('${listName}')/items?$select=AttachmentFiles/FileName,AttachmentFiles/FileNameAsPath,AttachmentFiles/ServerRelativeUrl,Id,OData___dID,Title&$expand=AttachmentFiles&$filter=Attachments+eq+1&$top=999`
    )
    .then(r => {
      const dID = r["OData___dID"];
      delete r["OData___dID"];
      return { ...r, dID };
    });

const getDestinationLists = listName =>
  axiosG
    .get(
      `/IT/_api/web/lists/getbytitle('${listName}')/items?$select=AttachmentFiles/FileName,AttachmentFiles/FileNameAsPath,AttachmentFiles/ServerRelativeUrl,Id,OData___dID,Title&$expand=AttachmentFiles&$top=999`
    )
    .then(response =>
      (response.data.value || response.value)
        .map(r => {
          const dID = r["OData___dID"];
          delete r["OData___dID"];
          return { ...r, dID };
        })
        .reduce((main, { dID, Id }) => ({ ...main, [dID]: Id }), {})
    );


// Add the file to the file collection in the Shared Documents folder.
const addFileToFolder = (
  fileName,
  serverRelativeUrlToFolder,
  arrayBuffer,
  appWebUrl,
  hostWebUrl
) => {
  // Construct the endpoint.
  var fileCollectionEndpoint = String.format(
    "{0}/_api/sp.appcontextsite(@target)/web/getfolderbyserverrelativeurl('{1}')/files" +
      "/add(overwrite=true, url='{2}')?@target='{3}'",
    appWebUrl,
    serverRelativeUrlToFolder,
    fileName,
    hostWebUrl
  );

  // Send the request and return the response.
  // This call returns the SharePoint file.
  return axiosP.post(fileCollectionEndpoint, arrayBuffer, {
    processData: false,
    headers: {
      accept: "application/json;odata=verbose",
      // "X-RequestDigest": jQuery("#__REQUESTDIGEST").val(),
      "content-length": arrayBuffer.byteLength
    }
  });
};

const getFileBinary = (url, FileServerRelativeUrl) => {
  let _url = `${url}_api/web/getfilebyserverrelativeurl('${FileServerRelativeUrl}')/`;
  // _url += `$value`;
  _url += `openbinarystream`;
  console.log(_url);

  return axiosG(_url, {
    headers: {
      Accept: "application/octet-stream",
      binaryStringResponseBody: true
    }
  });
};

const addFileBinaryToLibrary = (url, libraryName, fileName, arrayBuffer) => {
  let _url = url.trim().replace(/\/$/, "");
  _url += `/_api/web/getfilebyserverrelativeurl('${libraryName}')/`;
  _url += `Files/Add(url='${fileName}', overwrite=true)`;
  _url += `?$expand=ListItemAllFields,ListItemAllFields/ParentList`;

  console.log("addFileBinaryToLibrary: ", _url);

  return axiosP.post(_url, arrayBuffer, {
    binaryStringRequestBody: true,
    headers: {
      Accept: "application/json; odata=verbose",
      "content-length": arrayBuffer.length,
      binaryStringRequestBody: true
    }
  });
};

const addFileBinaryToList = (url, listName, id, fileName, arrayBuffer) => {
  let _url = url.trim().replace(/\/$/, "");
  fileName = fileName.replace(/[#`]/gim, "");
  _url += `/_api/web/lists/GetByTitle('${listName}')/items(${id})/`;
  _url += `AttachmentFiles/Add(FileName='${encodeURIComponent(fileName)}')`;
  // console.log("addFileBinaryToList: ", _url);
  return axiosP.post(_url, arrayBuffer, {
    // binaryStringRequestBody: true,
    headers: {
      Accept: "application/json; odata=verbose",
      binaryStringRequestBody: true
    }
  });
};

const handleSaveToPC = (arrayBuffer, fileName) => {
  console.log("\tBudgetAY.item.AttachmentFiles:", arrayBuffer);
  const blob = new Blob([arrayBuffer], { type: "application/octet-stream" });
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.download = fileName;
  link.href = url;
  link.click();
};

const onError = error => {
  console.log(error);
};

function delay() {
  return new Promise(resolve => setTimeout(resolve, 300));
}

export default class MigrateWithAttachments extends Component {
  render() {
    let _BudgetAY = {};
    let filemetadata = {};

    new Promise((re, rj) => {
      getDestinationLists("BudgetAY")
        .then(response => {
          _BudgetAY = response;
          re(_BudgetAY);
        })
        .catch(() => re(_BudgetAY));
    }).then(_BudgetAY => {
      console.log("_BudgetAY:", _BudgetAY);
      let chainItems = Promise.resolve(0);

      getSourceLists("BudgetAY").then(response => {
        // var i = 0;
        // const t = response.data.value.length;
        for (const item of response.data.value) {
          filemetadata[item.Id] = filemetadata[item.Id] || [];
          chainItems = chainItems.then(() => {
            console.log(
              `BudgetAY.item:${item.Id} With ${
                item.AttachmentFiles.length
              } attachments`
            );

            return new Promise(resolve => {
              let chainFiles = Promise.resolve(0);
              for (const element of item.AttachmentFiles) {
                const fileUrl = element.ServerRelativeUrl.replace(
                  /[#`]/gim,
                  ""
                );
                const fileName = element.FileName.trim()
                  .replace(/[#`]/gim, "")
                  .trim()
                  .replace(/\s\s+/g, " ")
                  .replace(/  +/g, " ")
                  .replace(/ /g, "-");

                chainFiles = chainFiles.then(() => {
                  const _url = `http://localhost:3090${fileUrl}`;
                  console.log(
                    // `${i}/${t} : `,
                    `${item.Id}===${_BudgetAY[item.Id]}`,
                    fileUrl
                  );
                  return new Promise(rs => {
                    var xhr = new XMLHttpRequest();
                    xhr.open("GET", _url);
                    xhr.responseType = "blob";
                    xhr.onload = () => {
                      addFileBinaryToList(
                        // "http://localhost:3090/sites/ModernRootSite/it",
                        "http://localhost:3090/it",
                        "BudgetAY",
                        _BudgetAY[item.Id],
                        // element.FileNameAsPath.DecodedUrl,
                        item.Id + fileName,
                        xhr.response
                      )
                        .then(() => {
                          console.log("\tUpload Completed");
                          rs(element);
                        })
                        .catch(err => {
                          try {
                            if (
                              err.response.data.error.code !==
                              "-2130575257, Microsoft.SharePoint.SPException"
                            ) {
                              filemetadata[item.Id].push(item.Id + fileName);
                              const url = URL.createObjectURL(xhr.response);
                              const link = document.createElement("a");
                              link.download = item.Id + fileName;
                              link.href = url;
                              link.click();
                              console.log("\tlocally saved");
                            } else {
                              console.log("\tIgnored");
                            }
                          } catch {
                            console.log(err);
                          } finally {
                            rs(element);
                          }
                        });
                    };
                    xhr.send();
                  });
                });
              }
              chainFiles = chainFiles.then(() => resolve(0));
            });
          });
        }
        chainItems = chainItems.then(() => {
          console.log("******************* BudgetAY.done *******************");
          console.log(JSON.stringify(filemetadata, null, 1));
          return Promise.resolve(0);
        });
      });
    });

    return <div>MigrateWithAttachments</div>;
  }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment