Programatically get connector URL to file

I have a similar although different question to this forum post. I have PDF files described by FileDescriptors, and would like to generate links to those files to pass to a third-party client-side PDF viewer to fetch. However, I do not know how to obtain such a link.

The following URL is not accepted by the PDF viewer as it encounters a parse error:

String url = URLEncoder.encode(globalConfig.getWebAppUrl() + "/download?f=" + file.getUuid() + "&s=" + userSessionSource.getUserSession().getId() + ".pdf", "UTF-8");

This gives a parse error as well:

SamlConnection saml = settings.getDefaultSAMLConnection();
String url = URLEncoder.encode(globalConfig.getWebAppUrl() + "/open?" + (saml != null ? "saml=" + saml.getSsoPath() + "&" : "") + "screen=base$File.download&params=file:" + file.getMetaClass() + "-" + file.getId().toString(), "UTF-8");

The following gives a 401: unauthorized since no access token is provided:

String url = URLEncoder.encode(globalConfig.getWebAppUrl() + "/rest/v2/files/444c95bb-ca22-42a1-9302-bc55afcd94ff", "UTF-8");

When I download a file through the CUBA app, I can see in my browser’s network page that a request was sent to a URL that looks like this:

 String url = URLEncoder.encode(globalConfig.getWebAppUrl() + "/APP/connector/0/1/download-a8b00f84-c866-4972-9994-86c10e104aaf/TestPDFfile.pdf", "UTF-8");

This url is accepted by the PDF viewer and the file is displayed properly. However, I don’t know how to get such a ‘connector’ link programmatically. Can you help me achieve this?

1 Like

Hi,
Do you need link to this file to be accessible anonymously, without authentication? Or only for users who are already logged in to the CUBA application?

If anonymously, then the best way is to write a custom Spring MVC controller.

This link should only work for logged in users who have read access to the PDF file.

@albudarov So how do you do this when its during a authenticated session and the file is to be provided to a JS component?

I think, you can implement a Vaadin RequestHandler to handle the specifically constructed file link.
RequestHandler allows to process request with access to the CUBA session. And to have access to the raw ServletResponse.

See Vaadin doc: Request Handlers | Advanced Topics | Framework | Vaadin 8 Docs

And example of usage in the “Social login” guide:
https://www.jmix.io/cuba-platform/guides/anonymous-access-and-social-login#handling_social_service_response

Thanks, this solved the issue! With the following code I managed to create a working link to a file, that also authenticates the user.

Configuration configuration = AppBeans.get(Configuration.class);
GlobalConfig globalConfig = configuration.getConfig(GlobalConfig.class);
FileLoader fileLoader = AppBeans.get(FileLoader.class);
UiComponents uiComponents = AppBeans.get(UiComponents.class);
Logger log = LoggerFactory.getLogger(FilePreview.class);

BrowserFrame viewer = uiComponents.create(BrowserFrame.class);
viewer.setId("viewer");
viewer.setWidth("100%");
container.add(viewer);
container.expand(viewer);
final String url = globalConfig.getWebAppUrl() + "/download-pdf/" + file.getUuid();
VaadinSession.getCurrent().addRequestHandler(
		(RequestHandler) (session, request, response) -> {
			String referer = ((HttpServletRequest) request).getHeader("referer");
			//For an unknown reason, request.getPathInfo() always returns "/". However, we can check the referrer instead
			if (referer != null && referer.equals(globalConfig.getWebAppUrl() + "/VAADIN/mypdfviewer/viewer.html?file=" + url)) {
				response.setContentType("application/pdf");
				String headerKey = "Content-Disposition";
				String headerValue = String.format("attachment; filename=\"%s\"", file.getName());
				response.setHeader(headerKey, headerValue);
				//Will throw exception if PDF file larger than 2GB. Larger files require rework of setContentLength to allow long instead of int
				response.setContentLength(Math.toIntExact(file.getSize()));
				OutputStream outStream = response.getOutputStream();
				AppContext.withSecurityContext(session.getAttribute(SecurityContext.class), () -> {
					try {
						IOUtils.copy(fileLoader.openStream(file), outStream);
					} catch (FileStorageException | IOException ex) {
						log.error("Could not load PDF file!", ex);
					}
				});
				return true; // We wrote a response
			} else
				return false; // No response was written
		});
viewer.setSource(RelativePathResource.class).setPath("VAADIN/mypdfviewer/viewer.html?file=" + url);
1 Like