SharePoint / PnP-JS-Core

Code moved to https://github.com/pnp/pnpjs. This repository is archived.
Other
379 stars 231 forks source link

List Item Attachment progress callback #773

Closed chanm003 closed 6 years ago

chanm003 commented 6 years ago

Category

[ ] Enhancement

[ ] Bug

[x] Question

Version

3.0.1

Expected / Desired Behavior / Question

var itemPnp = $pnp.sp.web.getList('/sites/dev01/Lists/Custom02').items.getById(101);
itemPnp.attachmentFiles.add('Attachment 01.txt', 'Content123')

I can see add method returns a AttachmentFileAddResult. Is it possible to subscribe to some sort of progress call back, so I can update a progress bar on my UI? If so, how would I accomplish this?

Thanks!

koltyakov commented 6 years ago

Hey Chan,

attachmentFiles.add is a promise, it should be chained with .then and .catch. List attachments are added in one call there are no progress callbacks for it.

For files upload (into the doc libs), there is addChunked method, this guy has progress callback. The method uploads document in multiple calls using offset chunks.

chanm003 commented 6 years ago

Thanks. Any plans to add this as an enhancement?

koltyakov commented 6 years ago

I have some concerns that chunked upload is even available for attachmentFiles in REST API itself. I should take a look into this later on.

patrick-rodgers commented 6 years ago

I do not believe that is supported for list attachments. If you need attachments that are large I would recommend a document library with a lookup column back to the list item to link them. Document libraries are optimized for large files more so than attachments.

chanm003 commented 6 years ago

add() works for me but not addChunked()

Following produces a HTTP 500 error:

.rootFolder.files.addChunked(fileName, blob, (evt: ChunkedFileUploadProgressData) => {
      console.log(evt);
});

This works fine:

.rootFolder.files.add(fileName, blob);

When I catch the error from the promise and print out stack property:

"ProcessHttpClientResponseException: Error making HttpClient request in queryable: [500] Internal Server Error
    at new ProcessHttpClientResponseException (webpack-internal:///../../../../sp-pnp-js/lib/utils/exceptions.js:24:28)
    at eval (webpack-internal:///../../../../sp-pnp-js/lib/odata/core.js:47:24)
    at ZoneDelegate.invoke (webpack-internal:///../../../../zone.js/dist/zone.js:388:26)
    at Object.onInvoke (webpack-internal:///../../../core/esm5/core.js:4948:33)
    at ZoneDelegate.invoke (webpack-internal:///../../../../zone.js/dist/zone.js:387:32)
    at Zone.run (webpack-internal:///../../../../zone.js/dist/zone.js:138:43)
    at eval (webpack-internal:///../../../../zone.js/dist/zone.js:858:57)
    at ZoneDelegate.invokeTask (webpack-internal:///../../../../zone.js/dist/zone.js:421:31)
    at Object.onInvokeTask (webpack-internal:///../../../core/esm5/core.js:4939:33)
    at ZoneDelegate.invokeTask (webpack-internal:///../../../../zone.js/dist/zone.js:420:36)"
koltyakov commented 6 years ago

What is the version of SharePoint? addChunked works for 2016/SPO, not 2013 for instance.

chanm003 commented 6 years ago

Using SPO. More specifically, I use Express as my local server and sp-rest-proxy to forward requests to SPO.

koltyakov commented 6 years ago

What version of the Proxy? I guess I added addChunked support related frow not so long ago. Please try the latest version.

chanm003 commented 6 years ago

Any reason why addChunked will not work for SP2013 on-prem?

We have NG1 code that interacts with a document' library's rootFolder/files endpoint on SP2013 production server. We use a third party NG1 service to make the request for us.

var url = document.location.protocol + '//' + document.location.hostname + ":" + document.location.port + 
            _spPageContextInfo.webServerRelativeUrl + "/_api/Web/Lists/GetByTitle('Mission Products')/RootFolder/Files/add(url='" + opts.fileName + "." + opts.fileExtension + "',overwrite=true)";

        return Upload.http({
            method: 'POST',
            url: url,
            data: opts.uploadedFile,
            binaryStringRequestBody: true,
            headers: {
                "accept": "application/json;odata=verbose",
                "X-RequestDigest": $("#__REQUESTDIGEST").val()
            },
        })
        .then(onSuccess, onError, (opts.progressCallback || onProgress));

Promise returned from Upload.http() allows us to specify three callback functions, the last of which allows us to monitor upload progress. This library is found at https://github.com/danialfarid/ng-file-upload

chanm003 commented 6 years ago

sp-rest-proxy@2.5.3

koltyakov commented 6 years ago

Add chunked support was added in 2.5.4. When somebody in the team tried it within React component targeted to SPO, then tests showed up that the same code does not work with 2013. I made an assumption that the REST API back in time didn't support it. Never dig deeper. =)

chanm003 commented 6 years ago

Do you know if addChunked internally does something different than the Upload.upload() method found here?

https://github.com/danialfarid/ng-file-upload/blob/master/src/upload.js

chanm003 commented 6 years ago

Now I have sp-rest-proxy@2.5.8, and I get further. I am uploading a 22924 byte file, so I set the chunkSize parameter to 10000. As you can see now I am receiving two ChunkedFileUploadProgressData objects, but I get an error before I get the third lastchunk .

koltyakov commented 6 years ago

As far as I know, Angular's $http client deals with streams, it's not a fetch/promise based. Can take a look at the implementation, I guess that some features from Angular $http client are used.

Could you please refer proxy's issue in proxy's repo? So we won't mix these two threads and projects.

Do you know if addChunked internally does something different than the Upload.upload() method found here?

Yes, addChunked works differently it manipulates with muptiple endpoints for upload continue and finish.

chanm003 commented 6 years ago

It looks like the NG1 third-party service doesn't actually make multiple HTTP requests with chunks. It simply makes a single HTTP request for the upload, but listens to events on the XmlHttpRequest:

var oReq = new XMLHttpRequest();
oReq.upload.addEventListener("progress", updateProgress);
oReq.upload.addEventListener("load", transferComplete);

Is there any way I can access these low-level details XMLHttpRequest and attach listeners? I saw that you have a Iibrary configuration section but couldn't find anything there.

koltyakov commented 6 years ago

I'm afraid there is no such possibility in Fetch client. Yes, Fetch is a modern replacement for XMLHttpRequest, but it is a downside of simplicity - missing some features.

UPD: Some details can be found here. If showing a progress is a must-have feature for the application, upload method should be implemented locally using raw REST and XMLHttpRequest with progress callback.

chanm003 commented 6 years ago

Thanks for the link. I will use your suggestion for this specific use-case.

Is there a low-level utility using sp-pnp-js that allows me to easily get the X-RequestDigest? I know the fluent API handles those details for us.

koltyakov commented 6 years ago

Can't check at the moment, but maybe something like this (blind coding alert):

import { HttpClient } from 'sp-pnp-js';
import { DigestCache } from 'sp-pnp-js/lib/net/digestcache';

const webUrl = 'https://contoso.sharepoint.com';

const digestCache = new DigestCache(new HttpClient());
digestCache.getDigest(webUrl).then(digest => {
  console.log(digest);
});
patrick-rodgers commented 6 years ago

You can do that or you could just steal the code from the DigestCache class and use it in your application if you want to avoid using the library. Might be easier. Another alternative would be to show a visual element like a spinner and remove it once the file is uploaded. Attachments should be fairly small so the upload time would be pretty quick.

patrick-rodgers commented 6 years ago

Going to close this as answered, please reopen should you need to continue the conversation. Thanks!