How to copy files between sites using JavaScript REST in Office365 / SharePoint 2013
http://techmikael.blogspot.in/2013/07/how-to-copy-files-between-sites-using.html
I’m currently playing with a POC for an App, and wanted to try to do the App as a SharePoint hosted one, only using JavaScript and REST.
The starting point was to call _vti_bin/ExcelRest.asmx on the host web from my app web, but this end-point does neither support CORS nor JSONP, so it can’t be used directly. My next thought was; Ok, let’s copy the file from the host web over to my app web, then call ExcelRest locally. Easier said than done!
While the final solution seems easy enough, the research, trial and error have taken me about 3 days. I’m now sharing this with you so you can spend your valuable time increasing the international GDP instead.
Note: If you want to copy files between two libraries on the same level, then you can use the copyTo method. http://server/site/_api/web/folders/GetByUrl('/site/srclib')/Files/getbyurl('madcow.xlsx')/copyTo(strNewUrl = '/site/targetlib/madcow.xlsx,bOverWrite = true)
Problem
Copy a file from a document library in one site to a document library in a different site using JavaScript and REST.
The code samples have URL’s using the App web proxy, but it’s easily modifiable for non-app work as well.
Step 1 – Reading the file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var
hostweburl = decodeURIComponent(getParameterByName(
'SPHostUrl'
));
var
appweburl = decodeURIComponent(getParameterByName(
'SPAppWebUrl'
));
var
fileContentUrl =
"_api/SP.AppContextSite(@target)/web/GetFileByServerRelativeUrl('/site/library/madcow.xlsx')/$value?@target='"
+ hostweburl +
"'"
;
var
executor =
new
SP.RequestExecutor(appweburl);
var
info = {
url: fileContentUrl,
method:
"GET"
,
binaryStringResponseBody:
true
,
success:
function
(data) {
//binary data available in data.body
var
result = data.body;
},
error:
function
(err) {
alert(JSON.stringify(err));
}
};
executor.executeAsync(info);
The important parameter here is setting binaryStringResponseBody to true. Without this parameter the response is being decoded as UTF-8 and the result in the success callback is garbled data, which leads to a corrupt file on save.
The binaryStringResponseBody parameter is not documented anywhere, but I stumbled upon binaryStringRequestbody in an msdn article which was used when uploading a file, and I figured it was worth a shot. Opening SP.RequestExecutor.debug.js I indeed found this parameter.
Step 2 – Patching SP.RequestExecutor.debug.js
Adding binaryStringResponseBody will upon return of the call cause a script error as seen in the figure below.
The method in question is reading over the response byte-by-byte from an Uint8Array, building a correctly encoded string. The issue is that it tries to concatenate to a variable named ret, which is not defined. The defined variable is named $v_0, and here we have a real bug in the script. The bug is there both in Office365 and SharePoint 2013 on-premise.
Luckily for us patching JavaScript is super easy. You merely override the methods involved somewhere in your own code before it’s being called. In the below sample it’s being called once the SP.RequestExecutor.js library has been loaded. The method named BinaryDecode is the one with the error, but you have to override more methods as the originator called is internalProcessXMLHttpRequestOnreadystatechange, and it cascades to calling other internal functions which can be renamed at random as the method names are autogenerated. (This happened for me today and I had to change just overrinding the first function).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
$.getScript(scriptbase +
"SP.RequestExecutor.js"
,
function
(){
SP.RequestExecutorInternalSharedUtility.BinaryDecode =
function
SP_RequestExecutorInternalSharedUtility$BinaryDecode(data) {
var
ret =
''
;
if
(data) {
var
byteArray =
new
Uint8Array(data);
for
(
var
i = 0; i < data.byteLength; i++) {
ret = ret + String.fromCharCode(byteArray[i]);
}
}
;
return
ret;
};
SP.RequestExecutorUtility.IsDefined =
function
SP_RequestExecutorUtility$$1(data) {
var
nullValue =
null
;
return
data === nullValue ||
typeof
data ===
'undefined'
|| !data.length;
};
SP.RequestExecutor.ParseHeaders =
function
SP_RequestExecutor$ParseHeaders(headers) {
if
(SP.RequestExecutorUtility.IsDefined(headers)) {
return
null
;
}
var
result = {};
var
reSplit =
new
RegExp(
'\r?\n'
);
var
headerArray = headers.split(reSplit);
for
(
var
i = 0; i < headerArray.length; i++) {
var
currentHeader = headerArray[i];
if
(!SP.RequestExecutorUtility.IsDefined(currentHeader)) {
var
splitPos = currentHeader.indexOf(
':'
);
if
(splitPos > 0) {
var
key = currentHeader.substr(0, splitPos);
var
value = currentHeader.substr(splitPos + 1);
key = SP.RequestExecutorNative.trim(key);
value = SP.RequestExecutorNative.trim(value);
result[key.toUpperCase()] = value;
}
}
}
return
result;
};
SP.RequestExecutor.internalProcessXMLHttpRequestOnreadystatechange =
function
SP_RequestExecutor$internalProcessXMLHttpRequestOnreadystatechange(xhr, requestInfo, timeoutId) {
if
(xhr.readyState === 4) {
if
(timeoutId) {
window.clearTimeout(timeoutId);
}
xhr.onreadystatechange = SP.RequestExecutorNative.emptyCallback;
var
responseInfo =
new
SP.ResponseInfo();
responseInfo.state = requestInfo.state;
responseInfo.responseAvailable =
true
;
if
(requestInfo.binaryStringResponseBody) {
responseInfo.body = SP.RequestExecutorInternalSharedUtility.BinaryDecode(xhr.response);
}
else
{
responseInfo.body = xhr.responseText;
}
responseInfo.statusCode = xhr.status;
responseInfo.statusText = xhr.statusText;
responseInfo.contentType = xhr.getResponseHeader(
'content-type'
);
responseInfo.allResponseHeaders = xhr.getAllResponseHeaders();
responseInfo.headers = SP.RequestExecutor.ParseHeaders(responseInfo.allResponseHeaders);
if
(xhr.status >= 200 && xhr.status < 300 || xhr.status === 1223) {
if
(requestInfo.success) {
requestInfo.success(responseInfo);
}
}
else
{
var
error = SP.RequestExecutorErrors.httpError;
var
statusText = xhr.statusText;
if
(requestInfo.error) {
requestInfo.error(responseInfo, error, statusText);
}
}
}
};
});
Step 3 – Uploading the file
The next step is to save the file in a library on my app web. The crucial part again is to make sure the data is being treated as binary, this time withbinaryStringRequestBody set to true. Make a note of the digest variable as well. On a page inheriting the SP masterpage you can get this value with $("#__REQUESTDIGEST").val(). If not then you have to execute a separate call to _api/contextinfo. The code for that is at the bottom of this post.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var
appweburl = decodeURIComponent(getParameterByName(
'SPAppWebUrl'
));
var
executor =
new
SP.RequestExecutor(appweburl);
var
info = {
url:
"_api/web/GetFolderByServerRelativeUrl('/appWebtargetFolder')/Files/Add(url='madcow.xlsx', overwrite=true)"
,
method:
"POST"
,
headers: {
"Accept"
:
"application/json; odata=verbose"
,
"X-RequestDigest"
: digest
},
contentType:
"application/json;odata=verbose"
,
binaryStringRequestBody:
true
,
body: arrayBuffer,
success:
function
(data) {
alert(
"Success! Your file was uploaded to SharePoint."
);
},
error:
function
(err) {
alert(
"Oooooops... it looks like something went wrong uploading your file."
);
}
};
executor.executeAsync(info);
Journey
I started out using jQuery.ajax for my REST calls, but I did not manage to get the encoding right no matter how many posts I read on this. I read through a lot on the following links which led me to the final solution:
- http://social.msdn.microsoft.com/Forums/sharepoint/en-US/02bfbcdc-73c8-4fa5-8967-cfd903a0d72e/javascript-client-object-model-openbinary-method – which got me started on reading files, but the encoding was wrong
- http://www.shillier.com/archive/2013/03/26/uploading-files-in-sharepoint-2013-using-csom-and-rest.aspx – which has good code and samples on uploading files with CSOM and REST, also mentioning that CSOM has a limit on files being 1.5mb, which is why I went for REST.
- http://msdn.microsoft.com/en-us/library/jj164022.aspx – General REST information
- http://blogs.msdn.com/b/uksharepoint/archive/2013/04/20/uploading-files-using-the-rest-api-and-client-side-techniques.aspx – Sample code for uploading using binaryStringRequestBody
Get the digest value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$.ajax({
url:
"_api/contextinfo"
,
type:
"POST"
,
contentType:
"application/x-www-url-encoded"
,
dataType:
"json"
,
headers: {
"Accept"
:
"application/json; odata=verbose"
,
},
success:
function
(data) {
if
(data.d) {
var
digest = data.d.GetContextWebInformation.FormDigestValue;
}
},
error:
function
(err) {
alert(JSON.stringify(err));
}
});
How to copy files between sites using JavaScript REST in Office365 / SharePoint 2013的更多相关文章
- 关于在SharePoint 2013(2010)中Javascript如何实现批量批准的自定义操作功能?
1.概述: SharePoint 2013(包括SharePoint 2010)提供了很方便的,多选的界面,但是很多操作还是不能批量进行,比如:批准的功能.如果您要解决方案不关心代码,那么请直接联系作 ...
- Xcode6 ADD Copy Files Build Phase 是灰色的
在学习的怎样写frameWork的时候,查看一个教程How to Create a Framework for iOS [一个中文翻译 创建自己的framework] 其中一个步骤就是添加一个Cop ...
- How do I copy files that need root access with scp
server - How do I copy files that need root access with scp? - Ask Ubuntuhttps://askubuntu.com/quest ...
- Gradle Goodness: Copy Files with Filtering
Gradle Goodness: Copy Files with Filtering Gradle's copy task is very powerful and includes filterin ...
- [MSDN] 使用 SharePoint 2013 中的 JavaScript 库代码完成基本操作
MSDN:http://msdn.microsoft.com/zh-cn/library/jj163201.aspx 了解如何编写代码以在 SharePoint 2013 中使用 JavaScript ...
- SharePoint 2013 中使用 JavaScript Like 和Unlike list item/page/document
SharePoint 2013中新增了很多社交功能,比如用户可以like/unlike 任何一个 list item/page/document,这是一个非常不错的功能. 但有时觉得like/unli ...
- [Forward]Visual Guide: Setting up My Sites in SharePoint 2013
from http://blog.sharedove.com/adisjugo/index.php/2012/07/25/visual-guide-setting-up-my-sites-in-sh ...
- [Bash] Move and Copy Files and Folders with Bash
In this lesson we’ll learn how to move and rename files (mv) and copy (cp) them. Move index.html to ...
- VS Copy Files after build
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <ItemGroup> ...
随机推荐
- wow7.1 xd 新手教程
本人第一次录游戏视频,很多地方说错了 第一节说奶量百万,其实是十万 目前上传去百度云,录了奶德,跟猫德 [https://pan.baidu.com/s/1jIsLlg6]
- Linux 时钟与计时器
对 Linux 系统来说,时钟和计时器是两个十分重要的概念.时钟反应的是绝对时间,也可认为是实时时间.计时器反应的则是相对时间,即相对于系统启动后的计时.操作系统内核需要管理运行时间(uptime)和 ...
- codeforces C. Bits(数学题+或运算)
题意:给定一个区间,求区间中的一个数,这个数表示成二进制的时候,数字1的个数最多! 如果有多个这样的数字,输出最小的那个! 思路:对左区间的这个数lx的二进制 从右往左将0变成1,直到lx的值大于右区 ...
- int.class 与 Integer.class
TYPE 表示的引用类型所对应的基本类型的Class对象!
- 【原创】C#搭建足球赛事资料库与预测平台(5) 赔率数据表设计1
本博客所有文章分类的总目录:http://www.cnblogs.com/asxinyu/p/4288836.html 开源C#彩票数据资料库系列文章总目录:http://www.cn ...
- LDPC编译码基本原理
LDPC编译码基本原理 学习笔记 V1.1 2015/02/18 LDPC编译码基本原理 概述 本文是个人针对LDPC的学习笔记,主要针对LDPC译码算法做了简要的总结.该版本主要致力 ...
- ruby -- 进阶学习(七)strong parameters之permitted.has_key
简单例子: params = ActionController::Parameters.new(user: { name: 'Francesco', age: 22, role: 'admin' }) ...
- 使用WinDbg调试SQL Server——入门
这篇文章我想探究下SQL Server里完全不同的领域:如果使用WinDbg(来自针对Windows的调试工具)调试SQL Server.在我们进入枯涩细节之前,我想详细解释下为什么选择这样晦涩的话题 ...
- idea上实现github代码同步
1.先将github远程仓库clone到本地 2.将本地仓库中的项目导入到idea中 3.如果你的项目代码不是放在仓库的根目录下,idea会识别到你的项目是在git仓库目录下,必须点击add root ...
- OpenProcess打开进程返回错误的问题
问题描述 项目中需要做一个小功能:能够查看系统中当前正在运行的进程的内存信息,如内存块类型.分配状态.访问权限等.如下图所示: 需要的信息和上图相差无几.说起来也不算太难,毕竟现成的API已经提供了. ...