Maneira mais simples de fornecer dados estáticos de fora do servidor de aplicativos em um aplicativo da web Java

131

Eu tenho um aplicativo Web Java em execução no Tomcat. Desejo carregar imagens estáticas que serão mostradas na interface da Web da Web e nos arquivos PDF gerados pelo aplicativo. Também novas imagens serão adicionadas e salvas através do upload via interface da Web.

Não é um problema fazer isso armazenando os dados estáticos no contêiner da Web, mas armazená-los e carregá-los de fora do contêiner da Web está me causando dor de cabeça.

Prefiro não usar um servidor da Web separado como o Apache para servir os dados estáticos neste momento. Também não gosto da idéia de armazenar as imagens em binário em um banco de dados.

Eu já vi algumas sugestões, como ter o diretório de imagens como um link simbólico apontando para um diretório fora do contêiner da web, mas será que essa abordagem funcionará nos ambientes Windows e * nix?

Alguns sugerem escrever um filtro ou um servlet para manipular a exibição da imagem, mas essas sugestões foram muito vagas e de alto nível, sem ponteiros para informações mais detalhadas sobre como fazer isso.

Janne
fonte

Respostas:

161

Eu já vi algumas sugestões, como ter o diretório de imagens como um link simbólico apontando para um diretório fora do contêiner da web, mas será que essa abordagem funcionará nos ambientes Windows e * nix?

Se você seguir as regras do caminho do sistema de arquivos * nix (ou seja, você usa barras invertidas exclusivamente como em /path/to/files), ele também funcionará no Windows sem a necessidade de mexer nas File.separatorconcatenações feias de strings. No entanto, ele seria verificado apenas no mesmo disco de trabalho a partir de onde esse comando foi chamado. Então, se Tomcat é, por exemplo, instalado em C:seguida, o /path/to/filesque realmente apontam para C:\path\to\files.

Se todos os arquivos estão localizados fora do aplicativo da Web e você deseja que o Tomcat os DefaultServletmanipule, tudo o que você basicamente precisa fazer no Tomcat é adicionar o seguinte elemento Context à tag /conf/server.xmlinside <Host>:

<Context docBase="/path/to/files" path="/files" />

Dessa forma, eles estarão acessíveis através http://example.com/files/.... O exemplo de configuração do GlassFish / Payara pode ser encontrado aqui e o exemplo de configuração do WildFly pode ser encontrado aqui .

Se você quer ter controle sobre a leitura / escrita de arquivos si mesmo, então você precisa criar um Servletpara isto que, basicamente, apenas recebe uma InputStreamdo arquivo no sabor de, por exemplo, FileInputStreame escreve-o para o OutputStreamdo HttpServletResponse.

Na resposta, você deve definir o Content-Typecabeçalho para que o cliente saiba qual aplicativo associar ao arquivo fornecido. E você deve definir o Content-Lengthcabeçalho para que o cliente possa calcular o progresso do download, caso contrário, será desconhecido. E, você deve definir o Content-Dispositioncabeçalho como attachmentse desejar uma caixa de diálogo Salvar como , caso contrário, o cliente tentará exibi-lo em linha. Por fim, basta escrever o conteúdo do arquivo no fluxo de saída da resposta.

Aqui está um exemplo básico desse servlet:

@WebServlet("/files/*")
public class FileServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        String filename = URLDecoder.decode(request.getPathInfo().substring(1), "UTF-8");
        File file = new File("/path/to/files", filename);
        response.setHeader("Content-Type", getServletContext().getMimeType(filename));
        response.setHeader("Content-Length", String.valueOf(file.length()));
        response.setHeader("Content-Disposition", "inline; filename=\"" + file.getName() + "\"");
        Files.copy(file.toPath(), response.getOutputStream());
    }

}

Quando mapeado em um url-patternde, por exemplo /files/*, você pode chamá-lo por http://example.com/files/image.png. Dessa forma, você pode ter mais controle sobre as solicitações do que as solicitações DefaultServlet, como fornecer uma imagem padrão ( if (!file.exists()) file = new File("/path/to/files", "404.gif")ou seja ). Também request.getPathInfo()é preferível usar o acima, request.getParameter()porque é mais amigável ao SEO e, caso contrário, o IE não escolherá o nome do arquivo correto durante o Salvar como .

Você pode reutilizar a mesma lógica para servir arquivos do banco de dados. Simplesmente substitua new FileInputStream()por ResultSet#getInputStream().

Espero que isto ajude.

Veja também:

BalusC
fonte
1
@ SalutonMondo: o caminho com menos esforço.
BalusC
@ BalusC, tentei o seguinte: <Context docBase="/path/to/images" path="/images" />no Windows, mas obtendo o caminho relativo à pasta webapps:C:\install\apache-tomcat-8.0.26\webapps\tmp] is not valid
ACV
No Windows, deve ser:<Context docBase="C:\tmp\" path="/images" />
ACV
E HTTP Status 404 - /images/quando estiver fazendo http://localhost:8080/images/:, MAS um arquivo específico de under tmpworks: http://localhost:8080/images/Tulips.jpgestá OK
ACV
Dessa forma, se eu colocar uma imagem dentro da pasta, o link para a imagem dará a resposta 404, a menos que você reinicie o servidor, existe algum motivo para isso?
Mateus Viccari
9

Você pode fazer isso colocando suas imagens em um caminho fixo (por exemplo: / var / images ou c: \ images), adicione uma configuração nas configurações do aplicativo (representadas no meu exemplo pela Settings.class) e carregue-as assim, em um HttpServletdos seus:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
FileInputStream fis = new FileInputStream(filename);

int b = 0;
while ((b = fis.read()) != -1) {
        response.getOutputStream().write(b);
}

Ou se você deseja manipular a imagem:

String filename = Settings.getValue("images.path") + request.getParameter("imageName")
File imageFile = new File(filename);
BufferedImage image = ImageIO.read(imageFile);
ImageIO.write(image, "image/png", response.getOutputStream());

então o código html seria <img src="imageServlet?imageName=myimage.png" />

Obviamente, você deve pensar em fornecer diferentes tipos de conteúdo - "image / jpeg", por exemplo, com base na extensão do arquivo. Além disso, você deve fornecer algum cache.

Além disso, você poderia usar esse servlet para a qualidade rescaling de suas imagens, fornecendo parâmetros largura e altura como argumentos, e usando image.getScaledInstance(w, h, Image.SCALE_SMOOTH), considerando o desempenho, é claro.

Bozho
fonte
2
Você realmente não precisa da API Java 2D para isso, ela adicionaria desnecessariamente mais sobrecarga. Basta ler um InputStream e gravar em OutputStream.
BalusC
1
Sim, comecei a resposta com a ideia de redimensionar e manipular outras coisas, mas acabei simplificando.
Bozho 28/11/2009
7

Adicione ao server.xml:

 <Context docBase="c:/dirtoshare" path="/dir" />

Ative o parâmetro de listagem de arquivos dir no web.xml:

    <init-param>
        <param-name>listings</param-name>
        <param-value>true</param-value>
    </init-param>
céu azul
fonte
Com a alteração no web.xml, posso obter uma lista de arquivos para enviá-lo para a caixa de seleção?
Tomasz Waszczyk
1
esta mudança será no web.xml do tomcat, não a sua aplicação
vsingh
Obrigado por isso! É necessário ativar o parâmetro de listagem de arquivos dir no web.xml?
dariru 7/09/18
6

Requisito: Acessando os recursos estáticos (imagens / vídeos. Etc.) de fora do diretório WEBROOT ou do disco local

Etapa 1:
Crie uma pasta em aplicativos da web do servidor tomcat., Digamos que o nome da pasta seja myproj

Etapa 2:
em myproj, crie uma pasta WEB-INF e crie um web.xml simples

código em web.xml

<web-app>
</web-app>

Estrutura de diretórios para as duas etapas acima

c:\programfile\apachesoftwarefoundation\tomcat\...\webapps
                                                            |
                                                            |---myproj
                                                            |   |
                                                            |   |---WEB-INF
                                                                |   |
                                                                    |---web.xml

Etapa 3:
Agora crie um arquivo xml com o nome myproj.xml no seguinte local

c:\programfile\apachesoftwarefoundation\tomcat\conf\catalina\localhost

CÓDIGO em myproj.xml:

<Context path="/myproj/images" docBase="e:/myproj/" crossContext="false" debug="0" reloadable="true" privileged="true" /> 

Passo 4:
4 A) Agora, crie uma pasta com o nome myproj em Mi unidade de disco rígido e criar um novo

pasta com imagens de nome e coloque algumas imagens na pasta de imagens (e:myproj\images\)

Suponhamos que myfoto.jpg seja colocado em e:\myproj\images\myfoto.jpg

4 B) Agora crie uma pasta com o nome WEB-INF e:\myproj\WEB-INFe crie um web.xml na pasta WEB-INF

Código no web.xml

<web-app>
</web-app>

Etapa 5:
agora crie um documento .html com o nome index.html e coloque em e: \ myproj

CODE sob index.html Bem-vindo ao Myproj

A estrutura de diretórios para as etapas 4 e 5 acima é a seguinte

E:\myproj
    |--index.html
    |
    |--images
    |     |----myfoto.jpg
    |
    |--WEB-INF
    |     |--web.xml

Etapa 6:
Agora inicie o servidor apache tomcat

Etapa 7:
abra o navegador e digite o URL da seguinte maneira

http://localhost:8080/myproj    

u exibe o conteúdo fornecido em index.html

Etapa 8:
Para acessar as imagens no disco rígido local (fora do webroot)

http://localhost:8080/myproj/images/myfoto.jpg
sbabamca
fonte
você pode me sugerir, como fazer a mesma coisa para valores dinâmicos. Quero dizer, quero gravar os dados (xml) no meu diretório local ou ee ler isso na minha página jsp. Existe alguma maneira de escrever no servidor gerenciado para o diretório para que eu os acesse usando o procedimento acima?
Sudip7
embora eu possa executar o arquivo index.html corretamente, mas não as imagens são visualizadas no navegador web
rogerwar
Meu post erro é trabalhar bem eu só esqueceu de colocar / no final do E: / myproj eu mudar isso para E: / myproj / e sua multa de trabalho Graças @sbabamca
rogerwar
Oi, Obrigado pelo post e é muito útil. Aqui eu desejo fazer upload de arquivos através da interface para esse diretório específico. Desejo ativar o método POST para o mesmo. Alguém por favor pode me ajudar no mesmo.
Vicky
6

Esta é a história do meu local de trabalho:
- Tentamos fazer upload de imagens multiplicadas e arquivos de documentos usando o Struts 1 e o Tomcat 7.x.
- Tentamos escrever arquivos enviados para o sistema de arquivos, nome do arquivo e caminho completo para os registros do banco de dados.
- Tentamos separar pastas de arquivos fora do diretório de aplicativos da web . (*)

A solução abaixo é bastante simples, eficaz para o requisito (*):

No arquivo META-INF/context.xmlarquivo com o seguinte conteúdo: (Exemplo, meu aplicativo é executado em http://localhost:8080/ABC, meu aplicativo / projeto é nomeado ABC). (este também é o conteúdo completo do arquivo context.xml)

<?xml version="1.0" encoding="UTF-8"?>
<Context path="/ABC" aliases="/images=D:\images,/docs=D:\docs"/>

(funciona com o Tomcat versão 7 ou posterior)

Resultado: fomos criados 2 alias. Por exemplo, salvamos imagens em: D:\images\foo.jpg e visualizamos no link ou usando a tag de imagem:

<img src="http://localhost:8080/ABC/images/foo.jsp" alt="Foo" height="142" width="142">

ou

<img src="/images/foo.jsp" alt="Foo" height="142" width="142">

(Eu uso o Netbeans 7.x, o Netbeans parece criar arquivo automaticamente WEB-INF\context.xml)

Do Nhu Vy
fonte
2

Se você decidir expedição para FileServlet, em seguida, você também vai precisar allowLinking="true"de context.xml, a fim de permitir FileServleta atravessar os links simbólicos.

Consulte http://tomcat.apache.org/tomcat-6.0-doc/config/context.html

cherouvim
fonte
0

se alguém não conseguir resolver o problema com a resposta aceita, observe as considerações abaixo:

  1. não há necessidade de mencionar localhost:<port>com <img> srcatributo.
  2. verifique se você está executando este projeto fora do eclipse, porque o eclipse cria context docBaseentrada por si próprio dentro de seu server.xmlarquivo local .
JPG
fonte
0

Leia o InputStream de um arquivo e grave-o ServletOutputStreampara enviar dados binários para o cliente.

  • Arquivo local Você pode ler um arquivo diretamente usando FileInputStream ('path / image.png') .
  • No arquivo Mongo DataBase, você pode obter o InputStream usando o GridFS .
@WebServlet("/files/URLStream")
public class URLStream extends HttpServlet {
    private static final long serialVersionUID = 1L;

    public URLStream() {
        super();
    }

    protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        File source = new File("D:\\SVN_Commit.PNG");
        long start = System.nanoTime();

        InputStream image = new FileInputStream(source);

        /*String fileID = request.getParameter("id");
        System.out.println("Requested File ID : "+fileID);
        // Mongo DB GridFS - https://stackoverflow.com/a/33544285/5081877
        image = outputImageFile.getInputStream();*/

        if( image != null ) {
            BufferedInputStream bin = null;
            BufferedOutputStream bout = null;
            ServletOutputStream sos = response.getOutputStream();
            try {
                bin = new BufferedInputStream( image );
                bout = new BufferedOutputStream( sos );
                int ch =0; ;
                while((ch=bin.read())!=-1) {
                    bout.write(ch);
                }
            } finally {
                bin.close();
                image.close();
                bout.close();
                sos.close();
            }

        } else {
            PrintWriter writer = response.getWriter();
            writer.append("Something went wrong with your request.");
            System.out.println("Image not available.");
        }
        System.out.println("Time taken by Stream Copy = "+(System.nanoTime()-start));
    }
}

Resultar o URL diretamente para o participante src.

<img src='http://172.0.0.1:8080/ServletApp/files/URLStream?id=5a575be200c117cc2500003b' alt="mongodb File"/>
<img src='http://172.0.0.1:8080/ServletApp/files/URLStream' alt="local file"/>

<video controls="controls" src="http://172.0.0.1:8080/ServletApp/files/URLStream"></video>
Yash
fonte
0

Se você deseja trabalhar com JAX-RS (por exemplo, RESTEasy), tente o seguinte:

@Path("/pic")
public Response get(@QueryParam("url") final String url) {
    String picUrl = URLDecoder.decode(url, "UTF-8");

    return Response.ok(sendPicAsStream(picUrl))
            .header(HttpHeaders.CONTENT_TYPE, "image/jpg")
            .build();
}

private StreamingOutput sendPicAsStream(String picUrl) {
    return output -> {
        try (InputStream is = (new URL(picUrl)).openStream()) {
            ByteStreams.copy(is, output);
        }
    };
}

usando javax.ws.rs.core.Responseecom.google.common.io.ByteStreams

electrobabe
fonte
-1

Fiz isso ainda mais simples. Problema: Um arquivo CSS tinha links de URL para a pasta img. Obtém 404.

Eu olhei para o URL, http: // tomcatfolder: port / img / blablah.png , que não existe. Mas isso está realmente apontando para o aplicativo ROOT no Tomcat.

Então, eu apenas copiei a pasta img do meu aplicativo da web no aplicativo ROOT. Trabalho!

Não é recomendado para produção, é claro, mas é para um aplicativo de desenvolvimento de ferramentas interno.

Josef.B
fonte