栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

动态创建的内容可供下载,而无需在Vaadin Flow Web应用程序的服务器端写入文件

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

动态创建的内容可供下载,而无需在Vaadin Flow Web应用程序的服务器端写入文件

警告:
我在这件事上不是专家。我在这里提供的示例代码似乎运行正常。通过研究有限的文档并阅读了网络上的许多其他文章,我将这种解决方案拼凑在一起。矿山可能不是最好的解决方案。


有关更多信息,请参见Vaadin手册的“
动态内容” 页面。

我们在您的问题中包含三个主要部分:

  • Vaadin Web应用程序页面上的小部件,可为用户提供下载。
  • 动态内容创建者
  • 在用户计算机上创建的文件的默认名称

我对前两个有解决方案,但对第三个没有解决方案。

下载小工具

如课题中所述,我们确实使用了

Anchor
小部件(请参阅Javadoc)。

我们在布局上定义一个成员变量。

private Anchor anchor;

我们通过传递一个

StreamResource
对象来实例化。此类在Vaadin中定义。它的工作是包装我们制作的类,该类将产生扩展Java类的实现
InputStream

输入流通过从其

read
方法返回
int
其值是预期的八位位组的数字,即0-255,一次提供一个八位位组的数据。到达数据末尾时,传回负数
read

在我们的代码中,我们实现了

makeStreamOfContent
一种充当
InputStream
工厂的方法。

private InputStream makeInputStreamOfContent ( ){    return GenerativeInputStream.make( 4 );}

在实例化我们的时

StreamResource
,我们传递了一个引用该方法的方法引用
makeInputStreamOfContent
。由于没有输入流或任何数据尚未生成,因此我们在这里变得有点抽象。我们只是在准备舞台;该动作稍后发生。

传递给的第一个参数

newStreamResource
是要在用户的客户端计算机上创建的文件的默认名称。在此示例中,我们使用的虚构名称
report.text

anchor =     new Anchor(         new StreamResource( "report.text" , this :: makeInputStreamOfContent ) ,         "Download generated content"     );

接下来,我们

download
在HTML5
anchor
元素上设置属性。此属性向浏览器指示当用户单击链接时我们打算下载目标。

anchor.getElement().setAttribute( "download" , true );

您可以通过将锚小部件包装在内来显示图标

Button

downloadButton = new Button( new Icon( VaadinIcon.DOWNLOAD_ALT ) );anchor.add( downloadButton );

如果使用这样的图标,则应从小

Anchor
部件中删除文本标签。而是将任何所需的文本放在中
Button
。因此,我们将空字符串(
""
newAnchor
传递给,并将标签文本作为第一个参数传递给
new Button

anchor =     new Anchor(         new StreamResource( "report.text" , this :: makeInputStreamOfContent ) ,         ""     );anchor.getElement().setAttribute( "download" , true );downloadButton =     new Button(         "Download generated content" ,         new Icon( VaadinIcon.DOWNLOAD_ALT )     );anchor.add( downloadButton );

动态内容创建者

我们需要实现一个

InputStream
子类,以提供给我们的下载小部件。

InputStream
抽象类提供了所有,但其中的一个方法的实现。我们只需要实现
read
满足项目需求的方法即可。

这是一种可能的实现方式。实例化

GenerativeInputStream
对象时,传递要生成的行数。一次生成一行数据,然后逐个八位字节地将数据提供给客户端。完成该行后,将生成另一行。因此,我们一次只处理一行就可以节省内存。

馈送到客户端的八位位组是构成我们行的UTF-8文本的八位位组。预期文本的每个字符都可以包含一个或多个八位字节。如果您不理解这一点,请阅读Joel
Spolsky撰写的有趣且内容丰富的文章,
《绝对最低限度每个软件开发人员绝对,肯定必须了解Unipre和字符集》(无借口!)

package work.basil.example;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.nio.charset.Charset;import java.time.Instant;import java.util.Objects;import java.util.Optional;import java.util.UUID;import java.util.function.IntSupplier;// Generates random data on-the-fly, to simulate generating a report in a business app.//// The data is delivered to the calling program as an `InputStream`. Data is generated// one line (row) at a time. After a line is exhausted (has been delivered octet by octet// to the client web browser), the next line is generated. This approach conserves memory// without materializing the entire data set into RAM all at once.//// By Basil Bourque. Use at your own risk.// © 2020 Basil Bourque. This source pre may be used by others agreeing to the terms of the ISC License.// https://en.wikipedia.org/wiki/ISC_licensepublic class GenerativeInputStream extends InputStream{    private int rowsLimit, nthRow;    InputStream rowInputStream;    private IntSupplier supplier;    static private String DELIMITER = "t";    static private String END_OF_LINE = "n";    static private int END_OF_DATA = - 1;    // --------|  Constructors  | -------------------    private GenerativeInputStream ( int countRows )    {        this.rowsLimit = countRows;        this.nthRow = 0;        supplier = ( ) -> this.provideNextInt();    }    // --------|  Static Factory  | -------------------    static public GenerativeInputStream make ( int countRows )    {        var gis = new GenerativeInputStream( countRows );        gis.rowInputStream = gis.nextRowInputStream().orElseThrow();        return gis;    }    private int provideNextInt ( )    {        int result = END_OF_DATA;        if ( Objects.isNull( this.rowInputStream ) )        { result = END_OF_DATA; // Should not reach this point, as we checked for null in the factory method and would have thrown an exception there.        } else  // Else the row input stream is *not*  null, so read next octet.        { try {     result = rowInputStream.read();     // If that row has exhausted all its octets, move on to the next row.     if ( result == END_OF_DATA )     {         Optional < InputStream > optionalInputStream = this.nextRowInputStream();         if ( optionalInputStream.isEmpty() ) // Receiving an empty optional for the input stream of a row means we have exhausted all the rows.         {  result = END_OF_DATA; // Signal that we are done providing data.         } else         {  rowInputStream = optionalInputStream.get();  result = rowInputStream.read();         }     } } catch ( IOException e ) {     e.printStackTrace(); }        }        return result;    }    private Optional < InputStream > nextRowInputStream ( )    {        Optional < String > row = this.nextRow();        // If we have no more rows, signal the end of data feed with an empty optional.        if ( row.isEmpty() )        { return Optional.empty();        } else        { InputStream inputStream = new ByteArrayInputStream( row.get().getBytes( Charset.forName( "UTF-8" ) ) ); return Optional.of( inputStream );        }    }    private Optional < String > nextRow ( )    {        if ( nthRow <= rowsLimit ) // If we have another row to give, give it.        { nthRow++; String rowString = UUID.randomUUID() + DELIMITER + Instant.now().toString() + END_OF_LINE; return Optional.of( rowString );        } else // Else we have exhausted the rows. So return empty Optional as a signal.        { return Optional.empty();        }    }    // --------|  `InputStream`  | -------------------    @Override    public int read ( ) throws IOException    {        return this.provideNextInt();    }}

默认文件名

我找不到完成最后一部分的方法,默认情况下文件的名称包含生成内容的时间。

我什至在这一点上发布了有关堆栈溢出的问题: 在Vaadin
Flow应用程序中使用文件名默认为用户事件的日期时间进行下载

问题在于,在加载页面并

Anchor
实例化该窗口小部件时,链接窗口小部件背后的URL仅创建一次。之后,在用户阅读页面时,时间流逝。当用户最终单击链接以开始下载时,当前时刻晚于URL中记录的时刻。

似乎没有简单的方法可以将该URL更新到用户的click事件或download事件的当前时刻。

提示

顺便说一下,对于实际工作,我不会使用自己的代码构建导出的行。相反,我将使用 Apache Commons
CSV之

类的库来编写制表符分隔或逗号分隔值(CSV)的内容。

资源资源

  • 论坛: Vaadin 10让用户下载文件
  • 论坛: 字节数组中的图像
  • 手册: 动态内容


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/438925.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号